home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / BAGOSRC.ARJ / BAGO.C < prev    next >
C/C++ Source or Header  |  1992-02-26  |  183KB  |  5,006 lines

  1. /* Notes to Localizers:
  2.  *  This program was designed to work specifically with English-language
  3.  *  words, but it is possible to adapt it to run in any given language
  4.  *  with an 8-bit character set.  Making this work with a 16-bit character
  5.  *  set would require redefining some basic structures.
  6.  *  A level 1 modification will make the game playable in another language:
  7.  *    1)  Translate the help file from BAGO.DOC, then recompile into BAGO.HLP.
  8.  *    2)  Assign new letters to the dice[][] array with a suitable distribution
  9.  *        for your language.
  10.  *    3)  Change hardwired strings [use your editor to search for double-
  11.  *        quote (") marks] to your language.
  12.  *    4)  Check that the string functions such as strupr(), toupper(), and
  13.  *        isupper() work properly for your language.
  14.  *    5)  You must discard the BAGO.DIC dictionary, but can generate a new
  15.  *        one simply by playing the game.
  16.  *    6)  Generate bitmaps for any additional characters you need.  See the
  17.  *        WM_PAINT case of the CubeButton window procedure.
  18.  *  A level 2 modification would involve making the suffixing routines work
  19.  *  properly in your language.
  20.  *    6)  Rewrite AddSuffix().
  21.  *    7)  Rewrite RemoveSuffix().
  22.  *    8)  May need to rewrite macros isdoub() and isvowel().
  23.  *    9)  May wish to rewrite isword(), or discard the function completely.
  24.  *   10)  Change AddQu() and RemoveQu() routines if necessary.  This is
  25.  *        because in English, 'U' always follows 'Q'.  You may need to write
  26.  *        some of your own routines to do similar things.
  27.  *   11)  Be aware that some of the 'reward' bitmaps may be illegal in some
  28.  *        countries, or at least illegal for distribution to minors.  You
  29.  *        may wish to discard this feature.
  30.  *
  31.  *  You may freely modify this source at no charge.  However, I would be
  32.  *  interested in seeing your results if you do.
  33.  */
  34. /****************************************************************************
  35.  
  36.     PROGRAM: Bago.c
  37.  
  38.     PURPOSE: Boggle game for MicroSoft Windows
  39.  
  40.     Author:  Roderick Young.  (Pen name H.G. Wrekshun)
  41.     
  42.     FUNCTIONS:
  43.  
  44.         WinMain() - calls initialization function, processes message loop
  45.         BagoInit() - initializes window data and registers window
  46.         BagoWndProc() - processes messages
  47.         About() - processes messages for "About" dialog box
  48.  
  49.     Modifications:
  50.         Q.00.00  Hello, World.
  51.         Q.00.01  First playable game.
  52.         Q.00.02  Added game timer.
  53.         Q.00.03  Added tree structure for dictionary.
  54.         Q.00.04  Added capability to write out dictionary.
  55.         Q.00.05  Added capability to read dictionary.  Added Resize function.
  56.         Q.00.06  Converted Egg Timer to its own window.
  57.         Q.00.07  Added dialog box for editing words in dictionary.
  58.         Q.00.08  Added routine to compute suffixes of words.
  59.         Q.00.09  Added Prev and Next to Edit dictionary dialog box
  60.         Q.00.10  Added Delete to Edit dictionary dialog box
  61.         Q.00.11  Added dictionary optimization.
  62.         Q.00.12  Added beep sound at end of game.
  63.         Q.00.13  Converted Egg Timer to autonomous Graphic hourglass window.
  64.         Q.01.00  Added window for computer play.
  65.         Q.01.01  Deleted rarely used -OUS, -ION, -ITY suffixes
  66.         Q.01.02  Added RemoveSuffix.  Added Learn mode.
  67.         Q.02.00  Added SearchBoard and psearch for computer word search.
  68.         Q.02.01  Eliminated flicker in updating of computer word list.
  69.         Q.02.02  Improved efficiency of psearch by 34% (killed more branches)
  70.         Q.02.03  Added suffix capability to computer search.
  71.                  Words found increased by 44%
  72.         Q.02.04  Changed optimize to use windows global memory.
  73.                  Search routine yield made to pass through keystrokes.
  74.         Q.02.05  A fixed bug in search algorithm.  Now finding 12% more words,
  75.                  search length up to 300% of previous.
  76.         Q.02.06  Checking for word validity added.
  77.         Q.02.07  Processing of Qu cube added.  Stored as 'Q' internally.
  78.         Q.02.08  General input routine added.  Dictionary cull added.
  79.         Q.02.09  Added Bago Cube font.
  80.         Q.02.10  Added variable difficulty level (smartness).  Improved font.
  81.         Q.02.11  Added rack numbers and ability to go back to certain rack.
  82.                  Also made general input box set default values.
  83.                  Added HELP reference card.
  84.         Q.02.12  Added statistics display and clear statistics.
  85.         Q.02.13  Added load and save rack.
  86.         Q.02.14  Added reset frequencies.
  87.                  Fixed problem of input words > 10 characters.
  88.         Q.02.15  Fixed deleting hTreeTop word bug.  Edit Dict dialog now
  89.                  initializes with a starter word.
  90.         Q.02.16  Avoid reading full dictionary if not enough heap space.
  91.                  Avoid learning words if not enough heap space.
  92.                  Save rack now includes both player and computer word lists.
  93.         Q.02.17  Question last user word if incomplete.  Fix cube spacing
  94.                  to work with Bago font on EGA.
  95.         Q.02.18  Computer search aborts when game over.
  96.         A.01.00  First release as Freeware.  New ABOUTBOX.
  97.                  Prev, Next, Virgin edit no longer blank current word.
  98.         A.01.01  Check for success on all GlobalAlloc.
  99.                  Width of Word list boxes now relative to character width.
  100.                  FindLeaf checks suffixes as well as root word.
  101.         A.01.02  Save and restore settings from WIN.INI
  102.         2.00     New version convention.  Accelerators added.
  103.         2.01     Clicking on hourglass now starts new game.
  104.         3.00     Port to windows 3.0.  ES_UPPERCASE added to edit window.
  105.         3.01     Fixed listbox windows to handle proportional fonts.
  106.                  Private profile file is now used.
  107.         3.02     Added isword() routine to loosely check for bogus words.
  108.         3.03     Added bitmap of eyes to menu, later to use for rewards.
  109.         3.04     Added progress box while loading dictionary.
  110.         3.05     Changed from special font to bitmapped controls for cubes.
  111.                  Cubes are still output only.
  112.         3.06     Moved RackNumber display from main window to title bar.
  113.         3.07     Added WinHelp file.
  114.         3.08     Made cube controls work for input.  Disable dictionary
  115.                  functions during a game.
  116.         3.09     Automatic prompts to save dictionary in places.
  117.                  Progress box added for Cull and Optimize.  Some message
  118.                  boxes taken out.
  119.         3.10     Got rid of annoying flash on STOP control.  Autodetect
  120.                  color/mono and do displays to suit.
  121.         3.11     Changed InputBox to use DialogBoxParam.  With 2.0 version,
  122.                  needed to use a global to pass input data.
  123.         3.12     Added Pic box to display reward pictorals.  Release to BBSes.
  124.         3.13     Fixed bug: main title getting messed up, not static.
  125.         3.14     Added tabstops so that scores line up right justified.
  126.         3.15     Added -ERS, -INGS suffixes.
  127.         3.16     Dict edit bug fixed - deleting leaf w/ no inferiors.
  128.                  Added DictChanged flag.  Distributed to BBses.
  129.         3.17     Added rand() when player types, so that computer won't
  130.                  always find the same words in a given game.
  131.         3.18     Feature of showing the path by which a word is formed.
  132.         3.19     Patch color depth detection to handle Super VGA and above.
  133.                  Compute initial sizing of window if none in .INI file.
  134.                  Focus set to word list after game (for keyboard-only players)
  135.                  TAB moves between list boxes (for KB-only users)
  136. ****************************************************************************/
  137.  
  138. #include <ctype.h>
  139. #include <stdlib.h>
  140. #include <math.h>      /* MUST have this, for drawing egg timer */
  141. #include <io.h>
  142. #include <windows.h>                /* required for all Windows applications */
  143. #include "bago.h"                   /* specific to this program              */
  144.  
  145. /* vowels include Y and W for my purpose */
  146. #define isvowel(c) (c=='A' || c=='E' || c=='I' || c=='O' || c=='U' || c=='W' || c=='Y')
  147. /* letters which can be doubled */
  148. #define isdoub(c) (!isvowel(c) && c!='H' && c!='J' && c!='Q' && c!='W' && c!='X' && c!='Y')
  149.  
  150. HANDLE hInst;                   /* current instance of main window */
  151. HWND hUEdit;                    /* edit window of words typed by player */
  152. HWND hUList = NULL;             /* listbox for player's words */
  153. HWND hCList = NULL;             /* Computer's word list window */
  154. HWND hEgg = NULL;               /* Handle for Egg Timer window */
  155. HWND hEnter;                    /* Enter and STOP keys */
  156. HWND hStop;
  157.  
  158. HANDLE hAccTable;               /* Handle for Menu accelerator table */
  159.  
  160. /* All the words in the dictionary are kept in a tree structure. */
  161. HANDLE hTreeTop;                /* the top of the tree containing the words */
  162.  
  163. HANDLE hCListTop;               /* top of tree for list of words computer found */
  164.  
  165. HCURSOR hHourGlass;              /* standard hourglass cursor */
  166.  
  167. HBITMAP hEyes;                  /* bitmap of eyes */
  168. HBRUSH hGrayBrush0, hGrayBrush1, hGrayBrush2;
  169. /* DEBUG make this a static local later, and pass to functions needing it */
  170. HANDLE hWord;                   /* Used by EditWord dialog box. */
  171.                                 /* Handle to record for word being processed */
  172.  
  173. /* Global variables for the game */
  174. /* A master list of the dice and what they look like on each face. */
  175. char dice[NDICE][7] = {
  176.         {0,'A','A','A','F','R','S'},
  177.         {0,'A','A','F','I','R','S'},
  178.         {0,'A','D','E','N','N','N'},
  179.         {0,'A','E','E','E','E','M'},
  180.         {0,'A','E','E','E','E','R'},
  181.         {0,'A','E','E','G','M','U'},
  182.         {0,'A','E','G','M','N','N'},
  183.         {0,'A','F','I','R','S','Y'},
  184.         {0,'B','J','K','Q','X','Z'},
  185.         {0,'C','C','E','N','S','T'},
  186.         {0,'C','E','I','I','L','T'},
  187.         {0,'C','E','I','L','P','T'},
  188.         {0,'C','E','I','P','S','T'},
  189.         {0,'D','D','H','N','O','T'},
  190.         {0,'D','H','H','L','O','R'},
  191.         {0,'D','H','L','N','O','R'},
  192.         {0,'D','H','L','N','O','R'},
  193.         {0,'E','I','I','I','T','T'},
  194.         {0,'E','M','O','T','T','T'},
  195.         {0,'E','N','S','S','S','U'},
  196.         {0,'F','I','P','R','S','Y'},
  197.         {0,'G','O','R','R','V','W'},
  198.         {0,'I','P','R','R','R','Y'},
  199.         {0,'N','O','O','T','U','W'},
  200.         {0,'O','O','O','T','T','U'} };
  201.  
  202. /* The playing board.  The master dice are shuffled into positions on
  203.  * this board at the start of each game.
  204.  */
  205. BOARD board[NROWS][NCOLS];
  206.  
  207. BOOL    UseTimer;       /* True if games are to be timed */
  208. BOOL    CPlay;          /* True if computer play option is enabled */
  209. BOOL    Learn;          /* True if computer is to learn words from user */
  210. BOOL    RotCubes;       /* True if cubes are to be rotated */
  211. BOOL    Sound;          /* True if sound enabled */
  212. BOOL    Rewards;        /* True if pictoral rewards are given */
  213. BOOL    Mono;           /* True if we are on monochrome display */
  214.  
  215. BOOL    GameOver;       /* True if game not presently in play */
  216.  
  217. int EndTime;            /* Game duration in seconds */
  218. int Smartness;          /* Level of challenge */
  219. int NGames = 0;         /* Number of games played */
  220. int RUScore = 0;        /* Running Player score */
  221. int RCScore = 0;        /* Running Computer score */
  222. int RUWords = 0;        /* Running Player # of words found */
  223. int RCWords = 0;        /* Running Computer # of words found */
  224.  
  225. int RackNumber, NextRackNumber;  /* Allows consistent regeneration of racks */
  226.                                 /* by forcing the RNG initialization */
  227.  
  228. char NextVal[NROWS][NCOLS];     /* These hold the values of the playing cubes */
  229. char NextOrient[NROWS][NCOLS];  /* for the next game.  This funny scheme is */
  230.                                 /* needed in order to restore an arbitrary */
  231.                                 /* game from a file */
  232.  
  233. BOOL    OptimizeFail;   /* TRUE if optimize GlobalAlloc failed */
  234.  
  235. int TScoreWidth;        /* Width of the string, "Total Score: " */
  236. int ListWinWidth;       /* Width of User and Computer list boxes */
  237. int TabStops[3];        /* For list box windows */
  238.  
  239. CUBELOC CubeStack[NDICE];     /* stack to keep track of cubes picked by user */
  240. int CubeStackPtr = 0;       /* points to first free space in stack */
  241.  
  242. BOOL DictChanged;       /* TRUE if dictionary has changed since loading */
  243.  
  244. /****************************************************************************
  245.  
  246.     FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
  247.  
  248.     PURPOSE: calls initialization function, processes message loop
  249.  
  250.     COMMENTS:
  251.  
  252.         This will initialize the window class if it is the first time this
  253.         application is run.  It then creates the window, and processes the
  254.         message loop until a PostQuitMessage is received.  It exits the
  255.         application by returning the value passed by the PostQuitMessage.
  256.  
  257. ****************************************************************************/
  258.  
  259. int PASCAL WinMain(hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
  260. HANDLE hInstance;                            /* current instance             */
  261. HANDLE hPrevInstance;                        /* previous instance            */
  262. LPSTR lpszCmdLine;                           /* command line                 */
  263. int nCmdShow;                                /* show-window type (open/icon) */
  264. {
  265.     HWND hWnd;                               /* window handle                */
  266.     MSG msg;                                 /* message                      */
  267.     char ProString[80];  /* Holds profile string */
  268.     int x0, y0, x1, y1;
  269.     int ColorDepth;  /* true color depth */
  270.     int VertRes;        /* Kludge way to determine EGA/VGA */
  271.     HWND hDesk;
  272.     HDC hDeskDC;
  273.  
  274.     /* find out some system metrics for sizing window and choosing bitmaps */
  275.     hDesk = GetDesktopWindow(); /* any window will do */
  276.     hDeskDC = GetDC(hDesk);
  277.     /* for sizing */
  278.     TScoreWidth = LOWORD(GetTextExtent(hDeskDC, "Total Score:    ", 16));
  279.     ListWinWidth = TScoreWidth + LOWORD(GetTextExtent(hDeskDC, "0000", 4))
  280.                            + GetSystemMetrics(SM_CXVSCROLL);
  281.     /* find out what kind of monitor we're on */
  282.     ColorDepth = GetDeviceCaps(hDeskDC, PLANES) * GetDeviceCaps(hDeskDC, BITSPIXEL);
  283.     VertRes = GetDeviceCaps(hDeskDC, VERTRES);
  284.     ReleaseDC(hDesk, hDeskDC);
  285.     Mono = (ColorDepth <= 2);
  286.  
  287.     /* must have brushes BEFORE setting window class */
  288.     if (ColorDepth < 3)
  289.        {
  290.         hGrayBrush0 = GetStockObject(WHITE_BRUSH);
  291.         hGrayBrush1 = hGrayBrush0;
  292.         hGrayBrush2 = GetStockObject(BLACK_BRUSH);
  293.        }
  294.     else if (VertRes < 480)     /* probably EGA */
  295.        {
  296.         hGrayBrush0 = CreateSolidBrush(GRAY1);
  297.         hGrayBrush1 = hGrayBrush0;
  298.         hGrayBrush2 = CreateSolidBrush(GRAY2);
  299.        }
  300.     else        /* assume VGA or better */
  301.        {
  302.         hGrayBrush0 = CreateSolidBrush(GRAY0);
  303.         hGrayBrush1 = CreateSolidBrush(GRAY1);
  304.         hGrayBrush2 = CreateSolidBrush(GRAY2);
  305.        }
  306.  
  307.     if (!hPrevInstance)                 /* Has application been initialized? */
  308.         if (!BagoInit(hInstance))
  309.             return (NULL);              /* Exits if unable to initialize     */
  310.  
  311.     hInst = hInstance;                  /* Saves the current instance        */
  312.  
  313.     /* Restore the last position, if any */
  314.     /* if no last position, compute a reasonable size and position */
  315.     if (GetPrivateProfileString("Bago", "Window", "", ProString, 80, BAGOINI))
  316.         sscanf(ProString, "%d %d %d %d", &x0, &y0, &x1, &y1);
  317.     else
  318.        {
  319.         x0 = 5;
  320.         y0 = 5;
  321.         x1 = 5 + 2*GetSystemMetrics(SM_CXFRAME)
  322.                  + 5*32 /* cubes */
  323.                  + 37   /* egg timer */
  324.                  + 140   /* slop around egg timer */
  325.                  + 2*ListWinWidth;
  326.         y1 = 5 + 2*GetSystemMetrics(SM_CYFRAME)
  327.                  + GetSystemMetrics(SM_CYCAPTION)
  328.                  + GetSystemMetrics(SM_CYMENU)
  329.                  + 5*32; /* cubes */
  330.        }
  331.  
  332.     hWnd = CreateWindow("Bago",                   /* window class            */
  333.         "Bago",                                   /* window name             */
  334.         WS_OVERLAPPEDWINDOW,                      /* window style            */
  335.         x0,                                       /* x position              */
  336.         y0,                                       /* y position              */
  337.         x1-x0,                                    /* width                   */
  338.         y1-y0,                                    /* height                  */
  339.         NULL,                                     /* parent handle           */
  340.         NULL,                                     /* menu or child ID        */
  341.         hInstance,                                /* instance                */
  342.         NULL);                                    /* additional info         */
  343.  
  344.     if (!hWnd)                                    /* Was the window created? */
  345.         return (FALSE);
  346.  
  347.     ShowWindow(hWnd, nCmdShow);                   /* Shows the window        */
  348.  
  349.     while (GetMessage(&msg,        /* message structure                      */
  350.             NULL,                  /* handle of window receiving the message */
  351.             NULL,                  /* lowest message to examine              */
  352.             NULL))                 /* highest message to examine             */
  353.        {
  354.         if (!TranslateAccelerator(hWnd, hAccTable, &msg))
  355.            {
  356.             TranslateMessage(&msg);    /* Translates virtual key codes */
  357.             DispatchMessage(&msg);     /* Dispatches message to window */
  358.            }
  359.        }
  360.     return (msg.wParam);           /* Returns the value from PostQuitMessage */
  361. }
  362.  
  363.  
  364. /****************************************************************************
  365.  
  366.     FUNCTION: BagoInit(HANDLE)
  367.  
  368.     PURPOSE: Initializes window data and registers window class
  369.  
  370.     COMMENTS:
  371.  
  372.         Sets up a structure to register the window class.  Structure includes
  373.         such information as what function will process messages, what cursor
  374.         and icon to use, etc.
  375.  
  376.  
  377. ****************************************************************************/
  378.  
  379. BOOL BagoInit(hInstance)
  380. HANDLE hInstance;                              /* current instance           */
  381. {
  382.     HANDLE hMemory;                            /* handle to allocated memory */
  383.     PWNDCLASS pWndClass;                       /* structure pointer          */
  384.     BOOL bSuccess;                             /* RegisterClass() result     */
  385.     OFSTRUCT OfStruct;
  386.  
  387.     /* the reason we alloc instead of just having automatic variable */
  388.     /* is to get the whole structure zeroed out */
  389.     hMemory = LocalAlloc(LPTR, sizeof(WNDCLASS));
  390.     pWndClass = (PWNDCLASS) LocalLock(hMemory);
  391.  
  392.     pWndClass->style = NULL;
  393.     pWndClass->lpfnWndProc = BagoWndProc;
  394.     pWndClass->hInstance = hInstance;
  395.     pWndClass->hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(BAGOICON));
  396.  
  397.     pWndClass->hCursor = LoadCursor(NULL, IDC_ARROW);
  398.     pWndClass->hbrBackground = hGrayBrush0;
  399.     pWndClass->lpszMenuName = MAKEINTRESOURCE(BAGOMENU);
  400.     pWndClass->lpszClassName = (LPSTR) "Bago";
  401.  
  402.     bSuccess = RegisterClass(pWndClass);
  403.  
  404.     if (!bSuccess)
  405.        {
  406.         LocalUnlock(hMemory);  /* unlock memory and return it to windows */
  407.         LocalFree(hMemory);
  408.         return(FALSE);  /* registering window failed */
  409.        }
  410.  
  411.     /* now register a class for the Egg Timer window */
  412.  
  413.     pWndClass->style = NULL;
  414.     pWndClass->lpfnWndProc = EggWndProc;
  415.     pWndClass->hInstance = hInstance;
  416.     pWndClass->hIcon = NULL;
  417.     pWndClass->hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(BAGOCUR));
  418.     pWndClass->hbrBackground = hGrayBrush1;
  419.     pWndClass->lpszMenuName = (LPSTR) NULL;
  420.     pWndClass->lpszClassName = (LPSTR) "Egg";
  421.  
  422.     bSuccess = RegisterClass(pWndClass);
  423.  
  424.     if (!bSuccess)
  425.        {
  426.         LocalUnlock(hMemory);  /* unlock memory and return it to windows */
  427.         LocalFree(hMemory);
  428.         return(FALSE);  /* registering window failed */
  429.        }
  430.  
  431.     /* The Progress Box class */
  432.  
  433.     pWndClass->style = NULL;
  434.     pWndClass->lpfnWndProc = ProWndProc;
  435.     pWndClass->hInstance = hInstance;
  436.     pWndClass->hIcon = NULL;
  437.     pWndClass->hCursor = NULL;
  438.     pWndClass->hbrBackground = hGrayBrush1;
  439.     pWndClass->lpszMenuName = NULL;
  440.     pWndClass->lpszClassName = "Pro";
  441.  
  442.     bSuccess = RegisterClass(pWndClass);
  443.  
  444.     if (!bSuccess)
  445.        {
  446.         LocalUnlock(hMemory);  /* unlock memory and return it to windows */
  447.         LocalFree(hMemory);
  448.         return(FALSE);  /* registering window failed */
  449.        }
  450.  
  451.     /* Cube button class */
  452.  
  453.     pWndClass->style = NULL;
  454.     pWndClass->lpfnWndProc = CubeWndProc;
  455.     pWndClass->hInstance = hInstance;
  456.     pWndClass->hIcon = NULL;
  457.     pWndClass->hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(BAGOCUR));
  458.     pWndClass->hbrBackground = NULL;
  459.     pWndClass->lpszMenuName = NULL;
  460.     pWndClass->lpszClassName = "CubeButton";
  461.  
  462.     bSuccess = RegisterClass(pWndClass);
  463.  
  464.     if (!bSuccess)
  465.        {
  466.         LocalUnlock(hMemory);  /* unlock memory and return it to windows */
  467.         LocalFree(hMemory);
  468.         return(FALSE);  /* registering window failed */
  469.        }
  470.  
  471.     /* Pictoral display class */
  472.  
  473.     pWndClass->style = NULL;
  474.     pWndClass->lpfnWndProc = PicWndProc;
  475.     pWndClass->hInstance = hInstance;
  476.     pWndClass->hIcon = NULL;
  477.     pWndClass->hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(BAGOCUR));
  478.     pWndClass->hbrBackground = hGrayBrush2;
  479.     pWndClass->lpszMenuName = NULL;
  480.     pWndClass->lpszClassName = "Pic";
  481.  
  482.     bSuccess = RegisterClass(pWndClass);
  483.  
  484.     LocalUnlock(hMemory);  /* unlock memory and return it to windows */
  485.     LocalFree(hMemory);
  486.  
  487.     return (bSuccess);           /* Returns result of registering the window */
  488. }
  489.  
  490. /****************************************************************************
  491.  
  492.     FUNCTION: BagoWndProc(HWND, unsigned, WORD, LONG)
  493.  
  494.     PURPOSE:  Processes messages
  495.  
  496.     MESSAGES:
  497.  
  498.         WM_CREATE     - create window
  499.         WM_PAINT      - repaint window
  500.         WM_DESTROY    - destroy window
  501.  
  502.     COMMENTS:
  503.  
  504.  
  505. ****************************************************************************/
  506.  
  507. long FAR PASCAL BagoWndProc(hWnd, message, wParam, lParam)
  508. HWND hWnd;                                /* window handle                   */
  509. unsigned message;                         /* type of message                 */
  510. WORD wParam;                              /* additional information          */
  511. LONG lParam;                              /* additional information          */
  512. {
  513.     switch (message) {
  514.         case WM_SYSCOMMAND:             /* message: command from system menu */
  515.             if (wParam == MN_REWARDS)
  516.                {
  517.                 ProcessBagoRewards(hWnd, wParam);
  518.                 break;
  519.                }
  520.             else                            /* Lets Windows process it       */
  521.                 return (DefWindowProc(hWnd, message, wParam, lParam));
  522.  
  523.         case WM_CREATE:                     /* message: window being created */
  524.             ProcessBagoCreate(hWnd, wParam, lParam);
  525.             break;
  526.  
  527.         case BAGOM_INIT:
  528.         /* A call to initialize.  This is only called once per instance. */
  529.         /* These actions are done here rather than at WM_CREATE message, */
  530.         /* because I want the window to appear immediately, rather than */
  531.         /* have an annoying pause while everything is initializing */
  532.  
  533.             LoadDictionary(hWnd);
  534.             break;
  535.  
  536.  
  537.         case WM_COMMAND:                /* process menu selection */
  538.             ProcessBagoCommand(hWnd, wParam, lParam);
  539.             break;
  540.  
  541.         case WM_PAINT:
  542.             ProcessBagoPaint(hWnd);
  543.             break;
  544.  
  545.         case WM_SIZE:
  546.             ProcessBagoSize(hWnd, wParam, lParam);
  547.             break;
  548.  
  549.         case WM_SETFOCUS:
  550.             /* This is in case the player came back from some other task */
  551.             if (!GameOver) SetFocus(hUEdit);
  552.             else
  553.                {
  554.                 if (CPlay) SetFocus(hCList);        /* so that keyboard only user */
  555.                 else       SetFocus(hUList);        /* can see word paths */
  556.                }
  557.             break;
  558.  
  559.         case BAGOM_CUBEDN:
  560.             /* given when a cube pressed down by player */
  561.             /* wparam contains CUBELOC of cube */
  562.             PushCube(wParam);
  563.             EnableAroundCube(wParam);
  564.             break;
  565.  
  566.         case BAGOM_CUBEUP:
  567.             /* given when a cube popped up by player */
  568.             EnableAroundCube(PopCube());
  569.             break;      
  570.  
  571.         case WM_DRAWITEM:
  572.             ProcessBagoDrawItem(hWnd, lParam);
  573.             break;
  574.  
  575.         case BAGOM_ENDGAME:
  576.             /* do not end the game if it is already over */
  577.             if (!GameOver) ProcessBagoEndGame(hWnd);
  578.             break;
  579.  
  580.         case WM_CLOSE:  /* message BEFORE window is destroyed */
  581.             ProcessBagoClose(hWnd);
  582.             DestroyWindow(hWnd);
  583.             break;
  584.  
  585.         case WM_DESTROY:                  /* message: window being destroyed */
  586.             PostQuitMessage(0);
  587.             break;
  588.  
  589.         default:                          /* Passes it on if unproccessed    */
  590.             return (DefWindowProc(hWnd, message, wParam, lParam));
  591.     }
  592.     return (NULL);
  593. }
  594.  
  595. /* Return a near pointer to a copy of a far string.
  596.  * Warnings:  will die on strings of length > 20.
  597.  *            will destroy the near copy of the string on subsequent calls
  598.  */
  599. char NEAR *FarToNearStr(FarStr)
  600. LPSTR FarStr;
  601.    {
  602.     static char NEAR NearStr[20];
  603.     char *NearPtr;
  604.     NearPtr = NearStr;
  605.     while (*NearPtr++ = *FarStr++);
  606.     return(NearStr);
  607.    }
  608.  
  609. /* CubeStackPtr points to first free space in the stack */
  610. PushCube(cube)
  611.     CUBELOC cube;
  612.    {
  613.     if (CubeStackPtr < NDICE)
  614.        {
  615.         CubeStack[CubeStackPtr] = cube;
  616.         CubeStackPtr++;
  617.        }
  618.    }
  619.  
  620. /* If the stack is empty, returns a virtual cube whose neighbors cannot */
  621. /* be in the rack.  This is important, as it will be used by the */
  622. /* EnableAroundCube() routine.  This is not a normal type pop routine */
  623. /* WARNING: returns the element REMAINING on the top of the stack, NOT */
  624. /* the element that was just popped off */
  625. CUBELOC PopCube()
  626.    {
  627.     CUBELOC OffBoard;
  628.  
  629.     if (CubeStackPtr > 0) CubeStackPtr--;
  630.     if (CubeStackPtr > 0) return(CubeStack[CubeStackPtr-1]);
  631.     else
  632.        {
  633.         OffBoard.row = -1; OffBoard.col = -1;
  634.         return(OffBoard);
  635.        }
  636.    }
  637.  
  638. /* This enables or disables all cube windows at once, according to the */
  639. /* value of the parameter passed.  */
  640. EnableCubes(bEnable)
  641.     BOOL bEnable;
  642.    {
  643.     int row, col;
  644.  
  645.     for (row=0; row < NROWS; row++)
  646.         for (col=0; col < NCOLS; col++)
  647.            {
  648.             EnableWindow(board[row][col].hWindow, bEnable);
  649.            }
  650.    }
  651.  
  652. /* This enables all neighbors to a cube except those which are */
  653. /* already pressed down.  Any other cubes in the rack are disabled */
  654. /* If the cube specified is not on the board, all cubes are enabled */
  655. EnableAroundCube(cube)
  656.     CUBELOC cube;
  657.    {
  658.     int row, col;
  659.     
  660.     if (cube.row < 0 || cube.col < 0)
  661.         EnableCubes(TRUE);
  662.     else
  663.        {
  664.         for (row=0; row < NROWS; row++)
  665.             for (col=0; col < NCOLS; col++)
  666.                {
  667.                 if (cube.col==col && cube.row==row) EnableWindow(board[row][col].hWindow, TRUE);
  668.                 else if ((cube.col==col-1 || cube.col==col+1 || cube.col==col) &&
  669.                          (cube.row==row-1 || cube.row==row+1 || cube.row==row))
  670.                     EnableWindow(board[row][col].hWindow, !board[row][col].Uused);
  671.                 else
  672.                     EnableWindow(board[row][col].hWindow, FALSE);
  673.                }
  674.        }
  675.    }
  676.  
  677. /* This routine sets up everything for a new game.  It scraps any existing */
  678. /* information in the word list windows, and displays a new board. */
  679. NewGame(hWnd)
  680. HWND  hWnd;
  681.    {
  682.     int row, col;  /* used to step through board array */
  683.     HANDLE hOldBuffer;  /* the User's word list */
  684.     static char Title[20];  /* static because will be in title */
  685.  
  686.     GameOver = FALSE;
  687.     CubeStackPtr = 0;  /* empty the Cube stack */
  688.  
  689.     /* disallow dictionary menu picks while game in progress */
  690.     EnableMenuItem(GetMenu(hWnd), 2, MF_BYPOSITION | MF_DISABLED | MF_GRAYED);
  691.     DrawMenuBar(hWnd);
  692.  
  693.     /* set up the rack, and report it in title bar */
  694.     RackNumber = NextRackNumber;
  695.     if (RackNumber >= 0)
  696.         sprintf(Title, "Bago - Game #%d", RackNumber);
  697.     else
  698.         /* Some games do not have rack numbers, like restored games */
  699.         strcpy(Title, "Bago");
  700.     /* not using SetWindowText because no rush */
  701.     PostMessage(hWnd, WM_SETTEXT, 0, (LONG)(LPSTR) Title);
  702.  
  703.     /* Now draw the new cubes */
  704.     for (row=0; row < NROWS; row++)
  705.         for (col=0; col < NCOLS; col++)
  706.            {
  707.             board[row][col].val = NextVal[row][col];
  708.             board[row][col].orient = NextOrient[row][col];
  709.             board[row][col].Uused = FALSE;
  710.             board[row][col].Cused = FALSE;
  711.             InvalidateRect(board[row][col].hWindow, NULL, FALSE);
  712.            }
  713.     EnableCubes(TRUE);
  714.     EnableWindow(hEnter, TRUE);
  715.     EnableWindow(hStop, TRUE);
  716.     EnableWindow(hUEdit, TRUE);
  717.  
  718.     /* Clear out the old word list from player's edit */
  719.     hOldBuffer = SendMessage(hUEdit, EM_GETHANDLE, 0, 0L);
  720.     if (hOldBuffer) LocalFree(hOldBuffer);
  721.     /* Allocate small buffer - edit control will expand if needed */
  722.     hOldBuffer = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, USERWORDLEN);
  723.     SendMessage(hUEdit, EM_SETHANDLE, hOldBuffer, 0L);
  724.     ShowWindow(hUEdit, SW_SHOW);
  725.     ShowWindow(hUList, SW_HIDE);
  726.  
  727.     SetFocus(hUEdit);  /* direct input to the word list */
  728.  
  729.     /* Clear player's list (it is under the edit window) */
  730.     SendMessage(hUList, LB_RESETCONTENT, 0, 0L);
  731.  
  732.     /* Start game timer if there is one */
  733.     if (hEgg) SendMessage(hEgg, EGGM_START, 0, 0L);
  734.  
  735.     /* If the computer is playing */
  736.     if (CPlay)
  737.        {
  738.         /* clear the computer's list */
  739.         SendMessage(hCList, LB_RESETCONTENT, 0, 0L);
  740.         DestroyLeaf(hCListTop);
  741.         hCListTop = NULL;
  742.         /* start computer search */
  743.         for (row=0; row < NROWS; row++)
  744.             for (col=0; col < NCOLS; col++)
  745.                 SearchBoard(&board[row][col]);
  746.        }
  747.    }
  748.  
  749. /* General Input */
  750. /* Prompt is the prompt string for the input, target output goes to integer
  751.  * OutI if Mode is TRUE, else string goes to OutString
  752.  * Values are returned in OutString and OutI.  Note: OutString must have
  753.  * space for 80 characters.
  754.  */
  755. /* Status is returned as a WORD */
  756. WORD InputBox(hWnd, Prompt, OutString, OutI, IntMode)
  757. HWND hWnd;
  758. LPSTR Prompt;
  759. LPSTR OutString;
  760. LPINT OutI;
  761. BOOL IntMode;
  762.    {
  763.     FARPROC lpInputDiag;
  764.     INPUTSTRUCT InputStruct;
  765.     WORD result;                /* either OK or CANCEL */
  766.  
  767.     InputStruct.Prompt = Prompt;
  768.     InputStruct.OutString = OutString;  /* default value for input */
  769.     InputStruct.OutI = OutI;
  770.     InputStruct.IntMode = IntMode;
  771.  
  772.     lpInputDiag = MakeProcInstance((FARPROC) InputDiag, hInst);
  773.     result = DialogBoxParam(hInst, MAKEINTRESOURCE(INPUTDIALOG), hWnd, lpInputDiag, (LONG) (LPSTR) &InputStruct);
  774.     FreeProcInstance(lpInputDiag);
  775.     return(result);
  776.    }
  777.  
  778. /* Tricky use of lParam -> Mode: upon entry, indicates integer or text mode */
  779. /* Upon exit, indicates the button (OK or CANCEL) pressed */
  780. BOOL FAR PASCAL InputDiag(hDlg, message, wParam, lParam)
  781. HWND hDlg;
  782. unsigned message;
  783. WORD wParam;
  784. LONG lParam;
  785.    {
  786.     BOOL TransOK;
  787.     static LPINPUTSTRUCT InputStruct;
  788.  
  789.     switch (message)
  790.        {
  791.         case WM_INITDIALOG:             /* message: initialize dialog box */
  792.             InputStruct = (LPINPUTSTRUCT) lParam;       /* remember our structure for later */
  793.             SetDlgItemText(hDlg, ID_IMSG, InputStruct -> Prompt);
  794.             SetFocus(GetDlgItem(hDlg, ID_INPUT));
  795.             /* set default value and select entire string */
  796.             SetDlgItemText(hDlg, ID_INPUT, InputStruct -> OutString);
  797.             SendMessage(GetDlgItem(hDlg, ID_INPUT),
  798.                 EM_SETSEL, 0, MAKELONG(0, 32767));
  799.             return (FALSE);     /* so windows won't arbitrarily set focus */
  800.             break;
  801.         case WM_COMMAND:                /* message: received a command */
  802.             switch (wParam)
  803.                {
  804.                 case ID_OK:
  805.                     if (InputStruct -> IntMode)
  806.                        {
  807.                         *(InputStruct -> OutI) = GetDlgItemInt(hDlg, ID_INPUT, &TransOK, TRUE);
  808.                         if (TransOK) EndDialog(hDlg, wParam);
  809.                        }
  810.                     else
  811.                        {
  812.                         GetDlgItemText(hDlg, ID_INPUT, InputStruct -> OutString, 80);
  813.                         EndDialog(hDlg, wParam);
  814.                        }
  815.                     break;
  816.                 case ID_CANCEL:
  817.                     EndDialog(hDlg, wParam);      /* Exits the dialog box */
  818.                     break;
  819.                }
  820.             return (TRUE);
  821.             break;
  822.         default:
  823.             return (FALSE);                   /* Didn't process a message    */
  824.        }
  825.    }
  826.  
  827. /**************** Bago Processing Routines ****************/
  828.  
  829. /* Creates the egg timer and puts it up on the screen */
  830. /* Warning: if the egg timer already exists, a second copy will be created */
  831. HWND CreateEggTimer(hWnd)
  832. HWND hWnd;
  833.    {
  834.     RECT Rect;
  835.     POINT Point;
  836.     HWND hEgg;
  837.  
  838.     GetClientRect(hWnd, (LPRECT) &Rect);
  839.     Point.x = Rect.left + 280;
  840.     Point.y = Rect.top;
  841.     ClientToScreen(hWnd, &Point);
  842.  
  843.     /* Would like to have WS_THICKFRAME, but that forces the */
  844.     /* resulting window to have an undesirably large minimum size */
  845.     hEgg = CreateWindow("Egg",          /* window class */
  846.         "",                             /* window name       */
  847.         WS_POPUP | WS_CAPTION | WS_BORDER | WS_VISIBLE,
  848.         Point.x,                        /* x position */
  849.         Point.y,                        /* y position */
  850.         37,                             /* width        */
  851.         120,                            /* height       */
  852.         hWnd,                           /* parent handle        */
  853.         NULL,                           /* no ID for popup window */
  854.         hInst,                          /* instance             */
  855.         NULL);                          /* additional info      */
  856.  
  857.     if (!hEgg)
  858.         MessageBox(hWnd, "Unable to create timer window", "ERROR",
  859.             MB_ICONQUESTION | MB_OK);
  860.  
  861.     return(hEgg);
  862.    }
  863.  
  864. ProcessBagoCommand (hWnd, wParam, lParam)
  865.  
  866. HWND    hWnd;
  867. WORD    wParam;
  868. LONG    lParam;
  869.    {
  870.     FARPROC lpEditWord;  /* proc instance for opening dialog box */
  871.     FARPROC lpProcAbout; /* pointer to the "About" function */
  872.     char OutBuf[80];
  873.     int NewValue;
  874.  
  875.     switch (wParam)
  876.        {
  877.         case MN_DD_GAME + MN_GAM_PLAY:
  878.             NewGame(hWnd);
  879.             break;
  880.             
  881.         case MN_DD_GAME + MN_GAM_END:
  882.             if (UseTimer) PostMessage(hEgg, EGGM_STOP, 0, 0L);
  883.             else PostMessage(hWnd, BAGOM_ENDGAME, 0, 0L);
  884.             break;
  885.  
  886.         case MN_DD_GAME + MN_GAM_STATS:
  887.             ProcessBagoStats(hWnd);
  888.             break;
  889.  
  890.         case MN_DD_GAME + MN_GAM_CSTATS:
  891.             RUScore = 0; RUWords = 0;
  892.             RCScore = 0; RCWords = 0;
  893.             NGames = 0;
  894.             break;
  895.  
  896.         case MN_DD_GAME + MN_GAM_TOURN:
  897.             sprintf(OutBuf, "%d", NextRackNumber);
  898.             if (InputBox(hWnd, "Set Rack Number for Next Game (0 - 32767)",
  899.                 OutBuf, &NewValue, TRUE) != ID_CANCEL)
  900.                {
  901.                 if (NewValue < 0)   NewValue = 0;
  902.                 if (NewValue > 32767) NewValue = 32767;
  903.                 NextRackNumber = NewValue;
  904.                 SetNextRack(NextRackNumber);
  905.                }
  906.             break;
  907.  
  908.         case MN_DD_GAME + MN_GAM_LOAD:
  909.             ProcessBagoGLoad(hWnd);
  910.             break;
  911.  
  912.         case MN_DD_GAME + MN_GAM_SAVE:
  913.             ProcessBagoGSave(hWnd);
  914.             break;
  915.  
  916.         case MN_DD_GAME + MN_GAM_QUIT:
  917.             ProcessBagoClose(hWnd);
  918.             DestroyWindow(hWnd);
  919.             break;
  920.  
  921.         /* Set the computer's skill level */
  922.         case MN_DD_OPTIONS + MN_OPT_LEVEL:
  923.             sprintf(OutBuf, "%d", Smartness);
  924.             if (InputBox(hWnd, "Difficulty (0 - 100)",
  925.                 OutBuf, &NewValue, TRUE) != ID_CANCEL)
  926.                {
  927.                 if (NewValue < 0)   NewValue = 0;
  928.                 if (NewValue > 100) NewValue = 100;
  929.                 Smartness = NewValue;
  930.                }
  931.             break;
  932.  
  933.         /* Realistically rotated cubes option */
  934.         case MN_DD_OPTIONS + MN_OPT_ROTATE:
  935.             ProcessBagoRotCubes(hWnd, wParam);
  936.             break;
  937.             
  938.         case MN_DD_OPTIONS + MN_OPT_SOUND:
  939.             ProcessBagoSound(hWnd, wParam);
  940.             break;
  941.             
  942.         /* Set game duration */
  943.         case MN_DD_OPTIONS + MN_OPT_GTIME:
  944.             sprintf(OutBuf, "%d", EndTime);
  945.             if (InputBox(hWnd, "Play Time in seconds (30 - 600)",
  946.                 OutBuf, &NewValue, TRUE) != ID_CANCEL)
  947.                {
  948.                 if (NewValue < 30)   NewValue = 30;
  949.                 if (NewValue > 600) NewValue = 600;
  950.                 EndTime = NewValue;
  951.                }
  952.             break;
  953.  
  954.         /* enable/disable egg timer */
  955.         case MN_DD_OPTIONS + MN_OPT_TIMER:
  956.             ProcessBagoTimer(hWnd, wParam);
  957.             break;
  958.             
  959.         case MN_DD_OPTIONS + MN_OPT_LEARN:
  960.             ProcessBagoLearn(hWnd, wParam);
  961.             break;
  962.  
  963.         /* Enable Computer Play */
  964.         case MN_DD_OPTIONS + MN_OPT_CPLAY:
  965.             ProcessBagoCPlay(hWnd, wParam);
  966.             break;
  967.  
  968.         /* display heap size */
  969.         case MN_DD_OPTIONS + MN_SEL_8:
  970.             sprintf(OutBuf, "Local Heap: %u\nGlobal Heap: %lu", LocalCompact(64000), GlobalCompact(1200000L));
  971.             MessageBox(hWnd, OutBuf, "Mem Free", MB_OK);
  972.             break;
  973.  
  974. #ifdef ignore
  975.         case MN_DD_OPTIONS + MN_SEL_9:
  976.             /* Test the search algorithm */
  977.             GameOver = FALSE;
  978.             DestroyLeaf(hCListTop);
  979.             hCListTop = NULL;
  980.             DebugCount = 0L;
  981.             /* start computer search */
  982.             for (Debugrow=0; Debugrow < NROWS; Debugrow++)
  983.                 for (Debugcol=0; Debugcol < NCOLS; Debugcol++)
  984.                     SearchBoard(&board[Debugrow][Debugcol]);
  985.             sprintf(OutBuf, "Iterations: %ld", DebugCount);
  986.             MessageBox(hWnd, OutBuf, "Test PSearch", MB_OK);
  987.             GameOver = TRUE;
  988. #endif
  989.             break;
  990.  
  991.         case MN_DD_DICT + MN_DIC_LOAD:
  992.             if (!LoadDictionary(hWnd))
  993.                 MessageBox(hWnd, "Cannot find "BAGODIC, "Error",
  994.                     MB_OK | MB_ICONQUESTION);
  995.             break;
  996.             
  997.         case MN_DD_DICT + MN_DIC_SAVE:
  998.             ProcessBagoSaveDict(hWnd);
  999.             break;
  1000.  
  1001.         case MN_DD_DICT + MN_DIC_SHOW:
  1002.             ProcessBagoShowDict(hWnd);
  1003.             InvalidateRect(hUEdit, NULL, TRUE);
  1004.             break;
  1005.             
  1006.         case MN_DD_DICT + MN_DIC_EDIT:
  1007.             lpEditWord = MakeProcInstance((FARPROC) EditWord, hInst);
  1008.             DialogBox(hInst, MAKEINTRESOURCE(EDITWORDBOX), hWnd, lpEditWord);
  1009.             FreeProcInstance(lpEditWord);
  1010.             break;
  1011.  
  1012.         case MN_DD_DICT + MN_DIC_OPT:
  1013.             ProcessBagoOptDict(hWnd);
  1014.             break;
  1015.  
  1016.         case MN_DD_DICT + MN_DIC_CULL:
  1017.             strcpy(OutBuf, "0");
  1018.             if (InputBox(hWnd, "Min Freq on words to KEEP (0-32767)",
  1019.                     OutBuf, &NewValue, TRUE) == ID_CANCEL)
  1020.                 break;
  1021.             CullDictionary(hWnd, NewValue);
  1022.             break;
  1023.         
  1024.         case MN_DD_DICT + MN_DIC_RFREQ:
  1025.             ResetFreqLeaf(hTreeTop);
  1026.             break;
  1027.         
  1028.         case MN_DD_HELP + MN_HLP_REFCARD:
  1029.             MessageBox(hWnd,
  1030.                 "Object of game: To make as many words as possible\n"
  1031.                 "     by following paths through adjacent letters.\n\n"
  1032.                 "Scoring:\n\n"
  1033.                 "   Word Size\tScore\n"
  1034.                 "   4 letters\t  1 point\n"
  1035.                 "   5 letters\t  2 points\n"
  1036.                 "   6 letters\t  3 points\n"
  1037.                 "   7 letters\t  5 points\n"
  1038.                 "   8 or more\t11 points\n\n"
  1039.                 "Disqualification marks on words:\n\n"
  1040.                 "(indent)\tFound by both players\n"
  1041.                 "   \"\tDuplicate of word listed earlier\n"
  1042.                 "   <\tLess than 4 letters\n"
  1043.                 "   ?\tNot in rack (check again)\n"
  1044.                 "   -\tNot a word\n\n\n"
  1045.                 "... select Help -> Index for more details",
  1046.                 "Reference Card", MB_OK);
  1047.             break;
  1048.             
  1049.         case MN_DD_HELP + MN_HLP_INDEX:
  1050.             if (!WinHelp(hWnd, BAGOHLP, HELP_INDEX, 0L))
  1051.                 MessageBox(hWnd, "Sorry, can't run help.", "Error", MB_OK);
  1052.             break;
  1053.  
  1054.         case MN_DD_HELP + MN_HLP_ABOUT:
  1055.             lpProcAbout = MakeProcInstance(About, hInst);
  1056.             DialogBox(hInst,                 /* current instance         */
  1057.                 MAKEINTRESOURCE(ABOUTBOX),   /* resource to use          */
  1058.                 hWnd,                        /* parent handle            */
  1059.                 lpProcAbout);                /* About() instance address */
  1060.             FreeProcInstance(lpProcAbout);
  1061.             break;
  1062.  
  1063.         case BAGOM_TAB:
  1064.             /* This command is so that keyboard-only users can swap between */
  1065.             /* lists after a game to see the word paths */
  1066.             if (GameOver && CPlay)
  1067.                {
  1068.                 if (GetFocus()==hCList) SetFocus(hUList);
  1069.                 else                    SetFocus(hCList);
  1070.                }
  1071.             break;
  1072.  
  1073.         case ID_STOP:
  1074.             /* Same as the endgame menu pick or accelerator */
  1075.             if (UseTimer) PostMessage(hEgg, EGGM_STOP, 0, 0L);
  1076.             else PostMessage(hWnd, BAGOM_ENDGAME, 0, 0L);
  1077.             break;
  1078.  
  1079.         case ID_ENTER:
  1080.             /* Enter key pressed.  If game in progress, send enter to */
  1081.             /* the user wordlist, and enable all cubes */
  1082.             if (!GameOver) ProcessEnter();
  1083.             break;
  1084.  
  1085.         case ID_USERWORDS:
  1086.             /* Randomize slightly so that computer plays differently based */
  1087.             /* on how player plays */
  1088.             rand();
  1089.             break;
  1090.  
  1091.         case ID_PLIST:
  1092.         case ID_CPLAY:
  1093.             /* click on word to display path on cubes */
  1094.             ProcessBagoPList(hWnd, lParam);
  1095.             break;
  1096.        }
  1097.    }
  1098.  
  1099. /* Process a notification message from a list box. */
  1100. /* Presently, this only shows that path (if any) that forms the selected */
  1101. /* word.  Process the message that selection changed. */
  1102. ProcessBagoPList(hWnd, lParam)
  1103. HWND hWnd;
  1104. LONG lParam;
  1105.    {
  1106.     if (HIWORD(lParam)==LBN_SELCHANGE)
  1107.        {
  1108.         HWND hListBox;
  1109.         int SelIndex;
  1110.         CUBELOC cube;
  1111.         int col, row;
  1112.         BOOL found;
  1113.         RECT rect;
  1114.         char WordSought[80];
  1115.         int tStackPtr;
  1116.  
  1117.          /* clean off any previous path from buttons */
  1118.         for (tStackPtr=CubeStackPtr; tStackPtr >= 0; tStackPtr--)
  1119.            {
  1120.             cube = CubeStack[tStackPtr];
  1121.             InvalidateRect(board[cube.row][cube.col].hWindow, NULL, FALSE);
  1122.            }
  1123.          /* get the requested word */
  1124.         hListBox = LOWORD(lParam);
  1125.         SelIndex = SendMessage(hListBox, LB_GETCURSEL, 0, 0L);
  1126.         if (SelIndex != LB_ERR)
  1127.            {
  1128.             SendMessage(hListBox, LB_GETTEXT, SelIndex, (LONG)(LPSTR) WordSought);
  1129.             TrimWord(WordSought, strlen(WordSought));
  1130.             RemoveQu(WordSought);
  1131.             /* Find the word on the board */
  1132.             found = FALSE;
  1133.             for (row=0; row<NROWS; row++)
  1134.                {
  1135.                 for (col=0; col<NCOLS; col++)
  1136.                    {
  1137.                     CubeStackPtr = 0;
  1138.                     if (verify(WordSought, &board[row][col], strlen(WordSought)))
  1139.                        {
  1140.                         found = TRUE;
  1141.                         break;
  1142.                        }
  1143.                    }
  1144.                 if (found) break;
  1145.                }
  1146.             if (found)
  1147.             /* tell main window to go and draw the path */
  1148.                {
  1149.                 rect.left=0; rect.top=0;
  1150.                 rect.right=32*NCOLS; rect.bottom=32*NROWS;
  1151.                 InvalidateRect(hWnd, &rect, FALSE);
  1152.                }
  1153.            }
  1154.        }
  1155.    }
  1156.  
  1157. /* Enter control pressed */
  1158. ProcessEnter()
  1159.    {
  1160.     CUBELOC cube;    
  1161.  
  1162.     /* pop up any cubes that are selected */
  1163.     if (CubeStackPtr > 0)
  1164.         while (CubeStackPtr > 0)
  1165.              {
  1166.               cube = CubeStack[--CubeStackPtr];
  1167.               board[cube.row][cube.col].Uused = FALSE;
  1168.               InvalidateRect(board[cube.row][cube.col].hWindow, NULL, FALSE);
  1169.              }
  1170.     EnableCubes(TRUE);
  1171.  
  1172.     PostMessage(hUEdit, WM_CHAR, '\r', 1L);
  1173.     SetFocus(hUEdit);
  1174.    }
  1175.  
  1176. /* Process WM_DRAWITEM message from STOP and ENTER controls */
  1177. ProcessBagoDrawItem(hWnd, lParam)
  1178. HWND hWnd;
  1179. LPDRAWITEMSTRUCT lParam;
  1180.    {
  1181.     HDC hDC;
  1182.     HBITMAP hBitmap, hOldBitmap;
  1183.     HDC hMemoryDC;
  1184.     
  1185.     hDC = lParam -> hDC;
  1186.     switch (lParam -> CtlID)
  1187.        {
  1188.         case ID_STOP:
  1189.             if ((lParam -> itemState) & ODS_SELECTED)
  1190.                 hBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(STOPDNBMP));
  1191.             else
  1192.                 hBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(STOPUPBMP));
  1193.             hMemoryDC = CreateCompatibleDC(hDC);
  1194.             hOldBitmap = SelectObject(hMemoryDC, hBitmap);
  1195.             if (hOldBitmap)
  1196.                {
  1197.                 BitBlt(hDC, 0, 0, 32, 32, hMemoryDC, 0, 0, SRCCOPY);
  1198.                 SelectObject(hMemoryDC, hOldBitmap);
  1199.                }
  1200.             DeleteObject(hBitmap);
  1201.             DeleteDC(hMemoryDC);
  1202.             break;
  1203.         case ID_ENTER:
  1204.             if ((lParam -> itemState) & ODS_SELECTED)
  1205.                 hBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(ENTERDNBMP));
  1206.             else
  1207.                 hBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(ENTERUPBMP));
  1208.             hMemoryDC = CreateCompatibleDC(hDC);
  1209.             hOldBitmap = SelectObject(hMemoryDC, hBitmap);
  1210.             if (hOldBitmap)
  1211.                {
  1212.                 BitBlt(hDC, 0, 0, 64, 32, hMemoryDC, 0, 0, SRCCOPY);
  1213.                 SelectObject(hMemoryDC, hOldBitmap);
  1214.                }
  1215.             DeleteObject(hBitmap);
  1216.             DeleteDC(hMemoryDC);
  1217.             break;
  1218.        }
  1219.    }
  1220.  
  1221. /* The only thing that is painted in the main window is the path that */
  1222. /* shows how a word was made.  And that feature is only available when */
  1223. /* the game is over */
  1224. ProcessBagoPaint(hWnd)
  1225. HWND hWnd;
  1226.    {
  1227.     HDC hDC;
  1228.     PAINTSTRUCT ps;
  1229.     HPEN hPathPen, hOldPen;
  1230.     int row, col;
  1231.     int tStackPtr;
  1232.     CUBELOC cube;
  1233.  
  1234.     hDC = BeginPaint(hWnd, (LPPAINTSTRUCT) &ps);
  1235.  
  1236.     if (GameOver && CubeStackPtr > 0)
  1237.        {
  1238.         /* Clear any previous path off the cubes if necessary */
  1239.         /* Must do all cubes, in case we are restoring a minimized window */
  1240.         for (row=0; row < NROWS; row++)
  1241.             for (col=0; col < NCOLS; col++)
  1242.                 UpdateWindow(board[row][col].hWindow);
  1243.  
  1244.         hPathPen = CreatePen(PS_SOLID, 1, RED);
  1245.         if (hPathPen)
  1246.            {
  1247.             hOldPen = SelectObject(hDC, hPathPen);
  1248.             tStackPtr = CubeStackPtr - 1;
  1249.             cube = CubeStack[tStackPtr];
  1250.             MoveTo(hDC, cube.col*32+16, cube.row*32+16);
  1251.             while (tStackPtr > 0)
  1252.                {
  1253.                 cube = CubeStack[--tStackPtr];
  1254.                 LineTo(hDC, cube.col*32+16, cube.row*32+16);
  1255.                }
  1256.             SelectObject(hDC, hOldPen);
  1257.             DeleteObject(hPathPen);
  1258.            }
  1259.         /* Note: tell ALL the buttons not to repaint */
  1260.         /* it is faster to just validate them all.  Selective validation */
  1261.         /* of the ones that the path went through may still allow some */
  1262.         /* of the buttons to repaint, taking up time */
  1263.         for (row=0; row < NROWS; row++)
  1264.             for (col=0; col < NCOLS; col++)
  1265.                 ValidateRect(board[row][col].hWindow, NULL);
  1266.        }
  1267.  
  1268.     EndPaint(hWnd, (LPPAINTSTRUCT) &ps);
  1269.    }
  1270.  
  1271. /* Process the Resize message by sizing the controls and child windows */
  1272. ProcessBagoSize(hWnd, wParam, lParam)
  1273. HWND hWnd;
  1274. WORD wParam;
  1275. LONG lParam;
  1276.    {
  1277.     MoveWindow(hUEdit, LOWORD(lParam) - 2*ListWinWidth, 0,
  1278.                        ListWinWidth, HIWORD(lParam), TRUE);
  1279.     MoveWindow(hUList, LOWORD(lParam) - 2*ListWinWidth, 0,
  1280.                        ListWinWidth, HIWORD(lParam), TRUE);
  1281.     MoveWindow(hCList, LOWORD(lParam) - ListWinWidth, 0,
  1282.                        ListWinWidth, HIWORD(lParam), TRUE);
  1283.     InvalidateRect(hWnd, NULL, TRUE);
  1284.    }
  1285.  
  1286. /* Converts all lowercase characters to uppercase, then eliminates any */
  1287. /* characters that are not strictly alphabetic */
  1288. TrimWord(w, len)
  1289. char w[80];
  1290. int len;
  1291.    {
  1292.     int source = 0, dest = 0;
  1293.  
  1294.     strupr(w);          /* convert to uppercase */
  1295.     for (source=0; source < len; source++)
  1296.         if (isupper(w[source]))
  1297.             w[dest++] = w[source];
  1298.  
  1299.     w[dest] = NULL;
  1300.    }
  1301.  
  1302. /* Converts 'Q' in a string to 'QU' */
  1303. AddQu(w)
  1304. char w[];
  1305.    {
  1306.     char temp[30];
  1307.     int source, dest;
  1308.     int len;
  1309.     
  1310.     len = strlen(w);
  1311.     dest = 0;
  1312.     for (source=0; source<len; source++)
  1313.        {
  1314.         temp[dest++] = w[source];
  1315.         if (w[source] == 'Q')
  1316.             temp[dest++] = 'U';
  1317.        }
  1318.     temp[dest] = NULL;
  1319.     strcpy(w, temp);
  1320.    }
  1321.  
  1322. /* Converts 'QU' in a string to 'Q' */
  1323. RemoveQu(w)
  1324. char w[];
  1325.    {
  1326.     int source, dest;
  1327.     int len;
  1328.     
  1329.     len = strlen(w);
  1330.     dest = 0;
  1331.     for (source=0; source<len; source++)
  1332.        {
  1333.         w[dest++] = w[source];
  1334.         if (w[source] == 'Q' && w[source+1] == 'U')
  1335.             source++;
  1336.        }
  1337.     w[dest] = NULL;
  1338.    }
  1339.  
  1340. /* This procedure is used to add a leaf to the dictionary tree */
  1341. /* Recursive, to match the tree search and sort */
  1342. /* Procedure is more complicated than a normal tree sort because
  1343.  * I have chosen to use Windows local heap, instead of direct pointers.
  1344.  * This will make the program slower in execution, but the program may
  1345.  * be much faster than it needs to be, anyway.
  1346.  * If the word is already in the structure, its frequency count is
  1347.  * incremented.  If not, the word is added to the structure.
  1348.  * When a word is added, a back link is made, too, so that the tree
  1349.  * may be easily traversed in either direction
  1350.  */
  1351. AddLeaf(s, hLeafPtr, freq, suffix, hSuperior)
  1352. char *s;
  1353. HANDLE *hLeafPtr;
  1354. int freq;
  1355. unsigned suffix;
  1356. HANDLE hSuperior;  /* handle of superior leaf */
  1357.    {
  1358.     PDW LeafPtr;
  1359.     int comp;  /* results of string compare */
  1360.     
  1361.     if (*hLeafPtr)
  1362.     /* A record exists at this handle.  Check it. */
  1363.        {
  1364.         LeafPtr = (PDW) LocalLock(*hLeafPtr);
  1365.         if (!(comp = strcmp(s, LeafPtr -> w)))
  1366.         /* words are equal, combine the relevant data */
  1367.            {
  1368.             /* don't want to overflow 16-bit integer */
  1369.             if ((LONG) (LeafPtr -> Freq) + freq < 32767L) LeafPtr -> Freq += freq;
  1370.             LeafPtr -> Suffix1 |= suffix;
  1371.            }
  1372.         else
  1373.            {
  1374.             if (comp < 0)
  1375.             /* the new word is less than the word at this leaf */
  1376.                {
  1377.                 AddLeaf(s, &(LeafPtr -> lt), freq, suffix, *hLeafPtr);
  1378.                }
  1379.             else
  1380.             /* the new word is greater than the word at this leaf */
  1381.                {
  1382.                 AddLeaf(s, &(LeafPtr -> gt), freq, suffix, *hLeafPtr);
  1383.                }
  1384.            }
  1385.         LocalUnlock(*hLeafPtr);
  1386.        }
  1387.     else
  1388.     /* No record exists at this handle.  Allocate memory and add word */
  1389.        {
  1390.         *hLeafPtr = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT,
  1391.                                 sizeof(DW));
  1392.         LeafPtr = (PDW) LocalLock(*hLeafPtr);
  1393.         strcpy(LeafPtr -> w, s);
  1394.         LeafPtr -> Freq = freq;
  1395.         LeafPtr -> Suffix1 = suffix;
  1396.         LeafPtr -> up = hSuperior;
  1397.         LocalUnlock(*hLeafPtr);
  1398.        }
  1399.    }
  1400.  
  1401. /* Relocate a leaf.  The assumption is made that hSource is the
  1402.  * handle of a valid record, and that this record does not already
  1403.  * exist in the tree.  hLeafPtr is the target destination for the
  1404.  * leaf.  If there is already something in hLeafPtr, the routine
  1405.  * recursively finds a free place.  hSuperior is a handle to the
  1406.  * leaf that the up link of hLeafPtr should point to.
  1407.  * This routine preserves any existing gt and lt links.
  1408.  */
  1409. RelocateLeaf(hSource, hLeafPtr, hSuperior)
  1410. HANDLE hSource, *hLeafPtr, hSuperior;
  1411.  
  1412.    {
  1413.     PDW LeafPtr, Source;
  1414.  
  1415.     if (*hLeafPtr)
  1416.     /* A record exists at this handle.  Check it. */
  1417.        {
  1418.         LeafPtr = (PDW) LocalLock(*hLeafPtr);
  1419.         Source =  (PDW) LocalLock(hSource);
  1420.  
  1421.         if (strcmp(Source -> w, LeafPtr -> w) < 0)
  1422.         /* the new word is less than the word at this leaf */
  1423.             RelocateLeaf(hSource, &(LeafPtr -> lt), *hLeafPtr);
  1424.         else
  1425.         /* the new word is greater than the word at this leaf */
  1426.             RelocateLeaf(hSource, &(LeafPtr -> gt), *hLeafPtr);
  1427.  
  1428.         LocalUnlock(*hLeafPtr);
  1429.         LocalUnlock(hSource);
  1430.        }
  1431.     else
  1432.     /* No record exists at this handle.  Put the record here */
  1433.        {
  1434.         *hLeafPtr = hSource;
  1435.         Source =  (PDW) LocalLock(hSource);
  1436.         Source -> up = hSuperior;
  1437.         LocalUnlock(hSource);
  1438.        }
  1439.    }
  1440.  
  1441. /* Similar to AddLeaf.  This procedure adds a leaf to a tree, giving
  1442.  * first sort priority to frequency, rather than the collating order
  1443.  * of the word.  This routine is used only to optimize the dictionary;
  1444.  * it is not used during normal play of the game.  Note that this
  1445.  * procedure does not make use of all the pointer fields in the DW
  1446.  * record, and uses lt and gt in a different manner than AddLeaf.
  1447.  * Count is incremented for each leaf added.
  1448.  */
  1449. AddLeafO(hSource, hLeafPtr)
  1450. HANDLE hSource;
  1451. LPHANDLE hLeafPtr;
  1452.    {
  1453.     LPDW LeafPtr;
  1454.     PDW SourcePtr;
  1455.     
  1456.     if (*hLeafPtr)
  1457.     /* A record exists at this handle.  Check it. */
  1458.        {
  1459.         LeafPtr = (LPDW) GlobalLock(*hLeafPtr);
  1460.         SourcePtr = (PDW) LocalLock(hSource);
  1461.         if (SourcePtr -> Freq == LeafPtr -> Freq)
  1462.         /* Frequencies equal, sort on basis of word comparison */
  1463.            {
  1464.             if (lstrcmp((LPSTR) (SourcePtr -> w), LeafPtr -> w) < 0)
  1465.                 AddLeafO(hSource, &(LeafPtr -> lt));
  1466.             else
  1467.                 AddLeafO(hSource, &(LeafPtr -> gt));
  1468.            }
  1469.         else
  1470.            {
  1471.             if (SourcePtr -> Freq > LeafPtr -> Freq)
  1472.             /* the new word is more frequent than the word at this leaf */
  1473.             /* we want more frequent to print first so use lt link */
  1474.                 AddLeafO(hSource, &(LeafPtr -> lt));
  1475.             else
  1476.             /* the new word is less frequent than the word at this leaf */
  1477.                 AddLeafO(hSource, &(LeafPtr -> gt));
  1478.            }
  1479.         LocalUnlock(hSource);
  1480.         GlobalUnlock(*hLeafPtr);
  1481.        }
  1482.     else
  1483.     /* No record exists here.  Allocate memory and copy entire record */
  1484.        {
  1485.         *hLeafPtr = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
  1486.                                 (DWORD) sizeof(DW));
  1487.         if (*hLeafPtr)
  1488.            {
  1489.             LeafPtr = (LPDW) GlobalLock(*hLeafPtr);
  1490.             SourcePtr = (PDW) LocalLock(hSource);
  1491.  
  1492.             lstrcpy(LeafPtr -> w, (LPSTR) (SourcePtr -> w));
  1493.             LeafPtr -> Freq = SourcePtr -> Freq;
  1494.             LeafPtr -> Suffix1 = SourcePtr -> Suffix1;
  1495.             LocalUnlock(hSource);
  1496.             GlobalUnlock(*hLeafPtr);
  1497.            }
  1498.         else
  1499.         /* Ouch! GlobalAlloc failed! */
  1500.             OptimizeFail = TRUE;
  1501.        }
  1502.    }
  1503.  
  1504. /* This procedure will print the entire tree out to a listbox */
  1505. /* Note that hLeafPtr is not passed by reference, as in Addleaf */
  1506. PrintLeaf(hList, hLeafPtr)
  1507. HWND hList;
  1508. HANDLE hLeafPtr;
  1509.    {
  1510.     PDW LeafPtr;
  1511.     char lbuf[40];  /* local printing buffer */
  1512.     
  1513.     if (hLeafPtr)
  1514.        {
  1515.         LeafPtr = (PDW) LocalLock(hLeafPtr);
  1516.         PrintLeaf(hList, LeafPtr -> lt);        /* print all less than */
  1517.         sprintf(lbuf, "%-10s %2d %4.4X \r\n",
  1518.                 LeafPtr -> w, LeafPtr -> Freq, LeafPtr -> Suffix1);
  1519.         SendMessage(hList, LB_INSERTSTRING, -1, (LONG) (LPSTR) lbuf);
  1520.         PrintLeaf(hList, LeafPtr -> gt);        /* print all greater than */
  1521.         LocalUnlock(hLeafPtr);
  1522.        }
  1523.    }
  1524.  
  1525. /* Count the number of leaves below and including the one pointed to */
  1526. /* by hLeafPtr, and return it */
  1527. int CountLeaf(hLeafPtr)
  1528. HANDLE hLeafPtr;
  1529.    {
  1530.     PDW LeafPtr;
  1531.     int n;
  1532.  
  1533.     if (!hLeafPtr) return(0);
  1534.     else
  1535.        {
  1536.         LeafPtr = (PDW) LocalLock(hLeafPtr);
  1537.         n = CountLeaf(LeafPtr -> lt) + 1 + CountLeaf(LeafPtr -> gt);
  1538.         LocalUnlock(hLeafPtr);
  1539.        }
  1540.     return(n);
  1541.    }
  1542.  
  1543. /* Similar to PrintLeaf.  This procedure copies a leaf and all its
  1544.  * inferior leaves into a second tree pointed to by hLeafPtr.
  1545.  * Used only to Re-Sort the tree into a different order for optimization
  1546.  */
  1547. ReSortLeaf(hSource, hLeafPtr, count, hPro)
  1548. HANDLE hSource;
  1549. LPHANDLE hLeafPtr;
  1550. int *count;
  1551. HWND hPro;
  1552.    {
  1553.     PDW SourcePtr;
  1554.     
  1555.     if (hSource && !OptimizeFail)
  1556.     /* Quick exit if pointing to null leaf, or GlobalAlloc failed in AddLeafO */
  1557.        {
  1558.         SourcePtr = (PDW) LocalLock(hSource);
  1559.         ReSortLeaf(SourcePtr -> lt, hLeafPtr, count, hPro);   /* copy all less than */
  1560.         AddLeafO(hSource, hLeafPtr);
  1561.         (*count)++;
  1562.         SendMessage(hPro, PRO_SETPOS, *count, 0L);
  1563.         ReSortLeaf(SourcePtr -> gt, hLeafPtr, count, hPro);   /* copy all greater than */
  1564.         LocalUnlock(hSource);
  1565.        }
  1566.    }
  1567.  
  1568. /* Leaf handle and all inferiors are copied in sorted order to a linear array.
  1569.  * Note that no data is copied; only handles.  hSource is the source handle,
  1570.  * LinArray is the destination, an array of handles.  Index specifies which
  1571.  * element of the array to copy to.  Index is automatically updated.
  1572.  */
  1573. CopyTreeToLin(hSource, LinArray, index)
  1574. HANDLE hSource;
  1575. LPHANDLE LinArray;
  1576. int *index;
  1577.    {
  1578.     LPDW SourcePtr;
  1579.  
  1580.     if (hSource)
  1581.        {
  1582.         SourcePtr = (LPDW) GlobalLock(hSource);
  1583.         CopyTreeToLin(SourcePtr->lt, LinArray, index);
  1584.         LinArray[*index] = hSource;
  1585.         (*index)++;
  1586.         CopyTreeToLin(SourcePtr->gt, LinArray, index);
  1587.         GlobalUnlock(hSource);
  1588.        }
  1589.    }
  1590.  
  1591. /* Attaches a range of elements in the linear array of handles in
  1592.  * such an order that they form a balanced binary tree.  first and
  1593.  * last are the bounds of the array to output, hLeafPtr is the
  1594.  * destination tree, and LinArray is the linear array of handles to
  1595.  * output.
  1596.  */
  1597. Balance(hLeafPtr, LinArray, first, last)
  1598. HANDLE *hLeafPtr;
  1599. LPHANDLE LinArray;
  1600. int first, last;
  1601.    {
  1602.     int mid;  /* midpoint between ends of segment */
  1603.  
  1604.     LPDW LeafPtr;   /* pointer to record being added */
  1605.     
  1606.     mid = (first+last)/2;
  1607.  
  1608.     /* Add the data from the leaf back into the dictionary */
  1609.     /* be careful, far to near copying */
  1610.     LeafPtr = (LPDW) GlobalLock(LinArray[mid]);
  1611.     AddLeaf(FarToNearStr(LeafPtr->w), hLeafPtr, LeafPtr->Freq,
  1612.         LeafPtr->Suffix1, NULL);
  1613.     GlobalUnlock(LinArray[mid]);
  1614.     
  1615.     if (first < mid) Balance(hLeafPtr, LinArray, first, mid-1);
  1616.     if (mid < last)  Balance(hLeafPtr, LinArray, mid+1, last);
  1617.    }
  1618.  
  1619. /* This procedure will print the entire tree out to the dictionary file */
  1620. /* Note that hLeafPtr is not passed by reference, as in Addleaf */
  1621. /* This is almost the same as the PrintLeaf function, except that */
  1622. /* the tree is printed in storage-order rather than collate-order */
  1623. BOOL PrintDict(hDictFile, hLeafPtr)
  1624. int hDictFile;
  1625. HANDLE hLeafPtr;
  1626.    {
  1627.     PDW LeafPtr;
  1628.     char lbuf[40];  /* local printing buffer */
  1629.     int IOStatus;
  1630.     BOOL retval;  /* return value */
  1631.     
  1632.     if (hLeafPtr)
  1633.        {
  1634.         LeafPtr = (PDW) LocalLock(hLeafPtr);
  1635.         sprintf(lbuf, "%-10s %2d %4.4X\r\n",
  1636.                 LeafPtr -> w, LeafPtr -> Freq, LeafPtr -> Suffix1);
  1637.         IOStatus = write(hDictFile, lbuf, strlen(lbuf));
  1638.  
  1639.         /* return FALSE if error */
  1640.         retval = (IOStatus == strlen(lbuf));
  1641.  
  1642.         /* print all less than */
  1643.         if (!PrintDict(hDictFile, (LeafPtr -> lt))) retval = FALSE;
  1644.         /* print all greater than */
  1645.         if (!PrintDict(hDictFile, (LeafPtr -> gt))) retval = FALSE;
  1646.         LocalUnlock(hLeafPtr);
  1647.         return(retval);
  1648.        }
  1649.    }
  1650.  
  1651. /* Find the string passed as a parameter in the tree */
  1652. /* If the string is found, a handle to the record is returned */
  1653. /* Else, NULL is returned */
  1654. HANDLE FindLeaf(Word, hLeafPtr)
  1655. char *Word;
  1656. HANDLE hLeafPtr;
  1657.    {
  1658.     PDW LeafPtr;
  1659.     int comp;           /* comparison result from strcmp */
  1660.     HANDLE retval;      /* return value */
  1661.     int nSuffix;                /* used for suffixing words */
  1662.     char FullWord[13];
  1663.     unsigned BitMask;
  1664.  
  1665.     if (!hLeafPtr) return(NULL);
  1666.     else
  1667.        {
  1668.         LeafPtr = (PDW) LocalLock(hLeafPtr);
  1669.         /* 2 character comparison only, for reasons outlined in psearch */
  1670.         comp = strncmp(Word, LeafPtr -> w, 2);
  1671.         if (comp < 0)
  1672.         /* the word is less than the word at this leaf */
  1673.             retval = FindLeaf(Word, LeafPtr -> lt);
  1674.         else if (comp > 0)
  1675.         /* the word is greater than the word at this leaf */
  1676.             retval = FindLeaf(Word, LeafPtr -> gt);
  1677.         else
  1678.         /* comp == 0, we could have a match */
  1679.            {
  1680.             if (!strcmp(Word, LeafPtr -> w))
  1681.             /* exact match */
  1682.                retval = hLeafPtr;
  1683.             else
  1684.               {
  1685.                /* must look at both branches */
  1686.                retval = FindLeaf(Word, LeafPtr -> lt);
  1687.                if (!retval) retval = FindLeaf(Word, LeafPtr -> gt);
  1688.                if (!retval && !strncmp(Word, LeafPtr->w, strlen(LeafPtr->w)-1))
  1689.                /* Last resort.  If enough letters in word match, try suffixes */
  1690.                   {
  1691.                    BitMask = 1;
  1692.     
  1693.                    for (nSuffix=ID_FIRSTSUF; nSuffix <= ID_LASTSUF; nSuffix++)
  1694.                       {
  1695.                        if (BitMask & (LeafPtr->Suffix1))
  1696.                        /* if the suffix is legal, try it */
  1697.                           {
  1698.                            AddSuffix(LeafPtr->w, nSuffix, FullWord);
  1699.                            if (!strcmp(Word, FullWord))
  1700.                               {
  1701.                                /* matches suffixed word exactly */
  1702.                                retval = hLeafPtr;
  1703.                                break;
  1704.                               }
  1705.                           }
  1706.                        BitMask <<= 1;  /* move to the next bit */
  1707.                       }
  1708.                    }
  1709.                }
  1710.             }
  1711.         LocalUnlock(hLeafPtr);
  1712.         return(retval);
  1713.        }
  1714.    }
  1715.  
  1716. /* This is used only in dictionary editing.  Finds exact word, ignoring
  1717.  * suffixes.  Returns the handle of the word.  If cannot find word,
  1718.  * returns NULL.  Otherwise, like FindLeaf()
  1719.  */
  1720. HANDLE FindExactLeaf(Word, hLeafPtr)
  1721. char *Word;
  1722. HANDLE hLeafPtr;
  1723.    {
  1724.     PDW LeafPtr;
  1725.     int comp;
  1726.     HANDLE retval;
  1727.  
  1728.     if (!hLeafPtr) return(NULL);
  1729.  
  1730.     LeafPtr = (PDW) LocalLock(hLeafPtr);
  1731.     comp = strcmp(Word, LeafPtr -> w);
  1732.     if (comp < 0)
  1733.         retval = FindExactLeaf(Word, LeafPtr -> lt);
  1734.     else if (comp > 0)
  1735.         retval = FindExactLeaf(Word, LeafPtr -> gt);
  1736.     else
  1737.         /* exact match */
  1738.         retval = hLeafPtr;
  1739.  
  1740.     LocalUnlock(hLeafPtr);
  1741.     return(retval);
  1742.    }
  1743.  
  1744. /* Follows the lt chain all the way down from the present record
  1745.  * and returns the last one.  Used to find the least word in
  1746.  * a chain.
  1747.  */
  1748. HANDLE FindLeast(hLeafPtr)
  1749.     HANDLE hLeafPtr;
  1750.    {
  1751.     PDW LeafPtr;
  1752.     HANDLE retval;
  1753.  
  1754.     LeafPtr = (PDW) LocalLock(hLeafPtr);
  1755.     if (LeafPtr -> lt) retval = FindLeast(LeafPtr -> lt);
  1756.     else retval = hLeafPtr;
  1757.     LocalUnlock(hLeafPtr);
  1758.     return(retval);
  1759.    }
  1760.  
  1761. /* Follows the up chain until it finds a word that is less
  1762.  * than the word corresponding to hLeafPtr.  If no such word is
  1763.  * found, NULL is returned.
  1764.  * WARNING: this routine assumes that hLeafPtr != NULL
  1765.  */
  1766. HANDLE FindUpLess(hLeafPtr)
  1767.     HANDLE hLeafPtr;
  1768.    {
  1769.     PDW LeafPtr;
  1770.     PDW SuperPtr;
  1771.     HANDLE retval;
  1772.  
  1773.     LeafPtr = (PDW) LocalLock(hLeafPtr);
  1774.     if (LeafPtr -> up)
  1775.     /* if there is a leaf above this one, examine it */
  1776.        {
  1777.         SuperPtr =  (PDW) LocalLock(LeafPtr -> up);
  1778.         if (strcmp(SuperPtr -> w, LeafPtr -> w) < 0)
  1779.         /* found one */
  1780.             retval = LeafPtr -> up;
  1781.         else
  1782.         /* look at next link up */
  1783.             retval = FindUpLess(LeafPtr -> up);
  1784.         LocalUnlock(LeafPtr -> up);
  1785.        }
  1786.     else
  1787.     /* there is no superior leaf, return null */
  1788.         retval = NULL;
  1789.         
  1790.     LocalUnlock(hLeafPtr);
  1791.     return(retval);
  1792.    }
  1793.         
  1794. /* Follows the up chain until it finds a word that is greater
  1795.  * than the word corresponding to hLeafPtr.  If no such word is
  1796.  * found, NULL is returned.
  1797.  * WARNING: this routine assumes that hLeafPtr != NULL
  1798.  */
  1799. HANDLE FindUpGreater(hLeafPtr)
  1800.     HANDLE hLeafPtr;
  1801.    {
  1802.     PDW LeafPtr;
  1803.     PDW SuperPtr;
  1804.     HANDLE retval;
  1805.  
  1806.     LeafPtr = (PDW) LocalLock(hLeafPtr);
  1807.     if (LeafPtr -> up)
  1808.     /* if there is a leaf above this one, examine it */
  1809.        {
  1810.         SuperPtr =  (PDW) LocalLock(LeafPtr -> up);
  1811.         if (strcmp(SuperPtr -> w, LeafPtr -> w) > 0)
  1812.         /* found one */
  1813.             retval = LeafPtr -> up;
  1814.         else
  1815.         /* look at next link up */
  1816.             retval = FindUpGreater(LeafPtr -> up);
  1817.         LocalUnlock(LeafPtr -> up);
  1818.        }
  1819.     else
  1820.     /* there is no superior leaf, return null */
  1821.         retval = NULL;
  1822.         
  1823.     LocalUnlock(hLeafPtr);
  1824.     return(retval);
  1825.    }
  1826.         
  1827. /* Follows the gt chain all the way down from the present record
  1828.  * and returns the last one.  Used to find the greatest word in
  1829.  * a chain.
  1830.  */
  1831. HANDLE FindGreatest(hLeafPtr)
  1832.     HANDLE hLeafPtr;
  1833.    {
  1834.     PDW LeafPtr;
  1835.     HANDLE retval;
  1836.  
  1837.     LeafPtr = (PDW) LocalLock(hLeafPtr);
  1838.     if (LeafPtr -> gt) retval = FindGreatest(LeafPtr -> gt);
  1839.     else retval = hLeafPtr;
  1840.     LocalUnlock(hLeafPtr);
  1841.     return(retval);
  1842.    }
  1843.  
  1844. /* Finds the previous leaf (collating order) in the tree, and
  1845.  * returns a handle to it.  If no leaf found, NULL is returned
  1846.  * Return value is in this priority:
  1847.  * 1.  If hLeafPtr is NULL, return NULL
  1848.  * 2.  If hLeafPtr has a lt link, return the greatest along this chain
  1849.  * 3.  If hLeafPtr has an up link, and the uplink was a gt link originally,
  1850.  *           return the uplink.
  1851.  * 4.  If none of the above, return NULL
  1852.  */
  1853. HANDLE FindPrev(hLeafPtr)
  1854.     HANDLE hLeafPtr;
  1855.    {
  1856.     PDW LeafPtr;
  1857.     PDW SuperiorPtr;
  1858.     HANDLE retval;
  1859.  
  1860.     if (!hLeafPtr) return(NULL);
  1861.     else
  1862.        {
  1863.         LeafPtr = (PDW) LocalLock(hLeafPtr);
  1864.         if (LeafPtr -> lt)
  1865.         /* if there is a lt link, look for the greatest along the chain */
  1866.             retval = FindGreatest(LeafPtr -> lt);
  1867.         else
  1868.         /* look higher in the tree to see if there is a lesser word */
  1869.             retval = FindUpLess(hLeafPtr);
  1870.             
  1871.         LocalUnlock(hLeafPtr);
  1872.         return(retval);
  1873.        }
  1874.    }
  1875.  
  1876. /* Finds the next leaf (collating order) in the tree, and
  1877.  * returns a handle to it.  If no leaf found, NULL is returned
  1878.  * Return value is in this priority:
  1879.  * 1.  If hLeafPtr is NULL, return NULL
  1880.  * 2.  If hLeafPtr has a gt link, return the least along this chain
  1881.  * 3.  If hLeafPtr has an up link, and the uplink was a lt link originally,
  1882.  *           return the uplink.
  1883.  * 4.  If none of the above, return NULL
  1884.  */
  1885. HANDLE FindNext(hLeafPtr)
  1886.     HANDLE hLeafPtr;
  1887.    {
  1888.     PDW LeafPtr;
  1889.     PDW SuperiorPtr;
  1890.     HANDLE retval;
  1891.  
  1892.     if (!hLeafPtr) return(NULL);
  1893.     else
  1894.        {
  1895.         LeafPtr = (PDW) LocalLock(hLeafPtr);
  1896.         if (LeafPtr -> gt)
  1897.         /* if there is a greater than link, look for the least along the chain */
  1898.             retval = FindLeast(LeafPtr -> gt);
  1899.         else
  1900.         /* look higher in the tree to see if there is a greater word */
  1901.             retval = FindUpGreater(hLeafPtr);
  1902.  
  1903.         LocalUnlock(hLeafPtr);
  1904.         return(retval);
  1905.        }
  1906.    }
  1907.  
  1908. /* Finds a virgin leaf in the tree, and returns the handle.  A virgin leaf is
  1909.  * one which has never been viewed by the dictionary editor.  This function is
  1910.  * to aid in finding words which do not yet have suffixes defined.
  1911.  */
  1912. HANDLE FindVirgin(hLeafPtr)
  1913.     HANDLE hLeafPtr;
  1914.    {
  1915.     PDW LeafPtr;
  1916.     HANDLE retval;
  1917.  
  1918.     if (!hLeafPtr) return(NULL);
  1919.     else
  1920.        {
  1921.         LeafPtr = (PDW) LocalLock(hLeafPtr);
  1922.         if ((LeafPtr -> Suffix1) & 0x8000)
  1923.         /* this leaf is virgin */
  1924.             retval = hLeafPtr;
  1925.         else
  1926.            {
  1927.             /* try the inferior leaves */
  1928.             if (!(retval = FindVirgin(LeafPtr -> lt)))
  1929.             retval = FindVirgin(LeafPtr -> gt);
  1930.            }
  1931.         LocalUnlock(hLeafPtr);
  1932.         return(retval);
  1933.        }
  1934.    }
  1935.  
  1936. /* This will free up the memory allocated to a leaf, and all leaves further
  1937.  * down the tree, whether on the greater-than or less-than side
  1938.  * The only time this should be called is to dispose of the entire dictionary
  1939.  * tree
  1940.  */
  1941. DestroyLeaf(hLeafPtr)
  1942. HANDLE hLeafPtr;
  1943.    {
  1944.     PDW LeafPtr;
  1945.     
  1946.     if (hLeafPtr)
  1947.        {
  1948.         LeafPtr = (PDW) LocalLock(hLeafPtr);
  1949.         DestroyLeaf(LeafPtr -> lt);     /* Destroy all less than */
  1950.         DestroyLeaf(LeafPtr -> gt);     /* Destroy all greater than */
  1951.         LocalUnlock(hLeafPtr);
  1952.         LocalFree(hLeafPtr);            /* Destroy self */
  1953.        }
  1954.    }
  1955.  
  1956. /* Like DestroyLeaf, but operates on a tree in Global (long pointer) memory */
  1957. DestroyGLeaf(hLeafPtr)
  1958. HANDLE hLeafPtr;
  1959.    {
  1960.     LPDW LeafPtr;
  1961.     
  1962.     if (hLeafPtr)
  1963.        {
  1964.         LeafPtr = (LPDW) GlobalLock(hLeafPtr);
  1965.         DestroyGLeaf(LeafPtr -> lt);     /* Destroy all less than */
  1966.         DestroyGLeaf(LeafPtr -> gt);     /* Destroy all greater than */
  1967.         GlobalUnlock(hLeafPtr);
  1968.         GlobalFree(hLeafPtr);            /* Destroy self */
  1969.        }
  1970.    }
  1971.  
  1972. /* Not 100% foolproof, but tries to take a guess as to the root word, given
  1973.  * a word which may have a suffix attached.
  1974.  * The guess is returned in the Dest parameter.
  1975.  * Returns handle to the word in dictionary if it was able to find the word
  1976.  * Warning: may barf on words shorter than 3 letters
  1977.  */
  1978. HANDLE RemoveSuffix(FullWord, Dest)
  1979. char FullWord[], Dest[];
  1980.    {
  1981.     int len;
  1982.     HANDLE hFound;
  1983.     PDW LeafPtr;
  1984.  
  1985.     strcpy(Dest, FullWord);
  1986.     len = strlen(Dest);
  1987.  
  1988.     /* First check the word by itself.  Many will have no suffix at all */
  1989.     hFound = FindLeaf(Dest, hTreeTop);
  1990.     if (hFound)
  1991.        {
  1992.         /* return only the root word */
  1993.         LeafPtr = (PDW) LocalLock(hFound);
  1994.         strcpy(Dest, LeafPtr -> w);
  1995.         LocalUnlock(hFound);
  1996.         return(hFound);
  1997.        }
  1998.  
  1999.     /* -S */
  2000.     if (Dest[len-1] == 'S')
  2001.        {
  2002.         /* -SSES */
  2003.         if (len > 3 && Dest[len-2] == 'E' && Dest[len-3] == 'S' & Dest[len-4] == 'S')
  2004.            {
  2005.             Dest[len-2] = NULL;
  2006.            }
  2007.         /* -IES */
  2008.         else if (Dest[len-2] == 'E' && Dest[len-3] == 'I')
  2009.            {
  2010.             Dest[len-3] = 'Y';
  2011.             Dest[len-2] = NULL;
  2012.            }
  2013.         /* -HES, -XES */
  2014.         else if (Dest[len-2] == 'E' && (Dest[len-3] == 'H' || Dest[len-3] == 'X'))
  2015.            {
  2016.             Dest[len-2] = NULL;
  2017.            }
  2018.         /* anything else but -SS */
  2019.         else if (Dest[len-2] != 'S')
  2020.            {
  2021.             Dest[len-1] = NULL;
  2022.            }
  2023.        }
  2024.     /* -ED, -ER, -EN*/
  2025.     else if (len > 4 && (Dest[len-1] == 'D' || Dest[len-1] == 'R' || Dest[len-1] == 'N') && Dest[len-2] == 'E')
  2026.        {
  2027.         /* ignore words less than 5 letters - probably not suffixed */
  2028.         /* -IED, -IER, -IEN */
  2029.         if (Dest[len-3] == 'I')
  2030.            {
  2031.             Dest[len-3] = 'Y';
  2032.             Dest[len-2] = NULL;
  2033.            }
  2034.         /* -XXED, -XXER, -XXEN, X=doubled letter */
  2035.         else if (Dest[len-3] == Dest[len-4] && Dest[len-3] != 'S' && Dest[len-3] != 'L')
  2036.            {
  2037.             Dest[len-3] = NULL;
  2038.            }
  2039.         /* -CVCED, -CVCER, -CVCEN, C=consonant, V=vowel */
  2040.         /* or -EED, -EER, -EEN */
  2041.         /* or -xSED, -xSER, -xSEN , x != S */
  2042.         else if (Dest[len-3] == 'E' || (Dest[len-3] == 'S' && Dest[len-4] != 'S') || (!isvowel(Dest[len-3]) && isvowel(Dest[len-4]) && !isvowel(Dest[len-5])))
  2043.            {
  2044.             Dest[len-1] = NULL;
  2045.            }
  2046.         else
  2047.            {
  2048.             Dest[len-2] = NULL;
  2049.            }
  2050.        }
  2051.     /* -Y, -LY, but not -AY */
  2052.     else if (Dest[len-1] == 'Y' && Dest[len-2] != 'A')
  2053.        {
  2054.         /* -LY */
  2055.         if (Dest[len-2] == 'L')
  2056.            {
  2057.             Dest[len-2] = NULL;
  2058.            }
  2059.         /* -XXY, X=doubled letter */
  2060.         else if (Dest[len-2] == Dest[len-3])
  2061.            {
  2062.             Dest[len-2] = NULL;
  2063.            }
  2064.         /* -CVCY, C=consonant, V=vowel */
  2065.         else if (len > 3 && !isvowel(Dest[len-2]) && isvowel(Dest[len-3]) && !isvowel(Dest[len-4]))
  2066.            {
  2067.             Dest[len-1] = 'E';
  2068.            }
  2069.         else
  2070.            {
  2071.             Dest[len-1] = NULL;
  2072.            }
  2073.        }
  2074.     /* -EST */
  2075.     /* do not consider small words like best, west, crest ... */
  2076.     else if (len > 5 && Dest[len-1] == 'T' && Dest[len-2] == 'S' && Dest[len-3] == 'E')
  2077.        {
  2078.         /* -IEST */
  2079.         if (Dest[len-4] == 'I')
  2080.            {
  2081.             Dest[len-4] = 'Y';
  2082.             Dest[len-3] = NULL;
  2083.            }
  2084.         /* -XXEST, X=doubled letter */
  2085.         else if (Dest[len-4] == Dest[len-5] && Dest[len-4] != 'L' && Dest[len-4] != 'S')
  2086.            {
  2087.             Dest[len-4] = NULL;
  2088.            }
  2089.         /* -CVCEST, C=consonant, V=vowel */
  2090.         /* or -EEST i.e. FREEST */
  2091.         else if (Dest[len-4] == 'E' || (!isvowel(Dest[len-4]) && isvowel(Dest[len-5]) && !isvowel(Dest[len-6])))
  2092.            {
  2093.             Dest[len-2] = NULL;
  2094.            }
  2095.         else
  2096.            {
  2097.             Dest[len-3] = NULL;
  2098.            }
  2099.        }
  2100.     /* -ING, -ISH */
  2101.     /* check len first so as not to process short words like sing, fish, bring */
  2102.     else if (len > 5 && ((Dest[len-1] == 'G' && Dest[len-2] == 'N') || (Dest[len-1] == 'H' && Dest[len-2] == 'S')) && Dest[len-3] == 'I')
  2103.        {
  2104.         /* -XXING, -XXISH, X=doubled letter, except -SSING, -SSISH */
  2105.         if (Dest[len-4] == Dest[len-5] && Dest[len-4] != 'S' && Dest[len-4] != 'L')
  2106.            {
  2107.             Dest[len-4] = NULL;
  2108.            }
  2109.         /* -CVCING, -CVCISH, C=consonant, V=vowel */
  2110.         else if (!isvowel(Dest[len-4]) && isvowel(Dest[len-5]) && !isvowel(Dest[len-6]))
  2111.            {
  2112.             Dest[len-3] = 'E';
  2113.             Dest[len-2] = NULL;
  2114.            }
  2115.         else
  2116.            {
  2117.             Dest[len-3] = NULL;
  2118.            }
  2119.        }
  2120.     return(FindLeaf(Dest, hTreeTop));
  2121.    }
  2122.  
  2123. /* increments the frequency of the word at hLeaf */
  2124. IncFreq(hLeaf)
  2125. HANDLE hLeaf;
  2126.    {
  2127.     PDW LeafPtr;
  2128.     
  2129.     LeafPtr = (PDW) LocalLock(hLeaf);
  2130.     if (LeafPtr -> Freq < 32767) (LeafPtr -> Freq)++;
  2131.     LocalUnlock(hLeaf);
  2132.    }
  2133.  
  2134. /* Verify that a word is actually on the board, returning TRUE if it is */
  2135. /* Searches the word BACKWARDS (no reason) */
  2136. /* Something like SearchBoard, only much faster */
  2137. /* This routine also doubles as a path-finder for displaying where a given */
  2138. /* word is on the board.  To use in this manner, first clear out the cube */
  2139. /* stack by setting CubeStackPtr = 0.  If Verify returns TRUE, then the */
  2140. /* cubes forming the word are left on the stack, in order */
  2141. BOOL Verify(TrialW, CubePtr, TrialLen)
  2142. char *TrialW;
  2143. BOARD *CubePtr;
  2144. int TrialLen;
  2145.    {
  2146.     int Neighbor;
  2147.     CUBELOC cube;
  2148.     
  2149.     if (CubePtr == NULL || (CubePtr -> Cused))
  2150.         return(FALSE);
  2151.  
  2152.     if (TrialLen==1)
  2153.        {
  2154.         if (*TrialW == CubePtr -> val)
  2155.            {
  2156.             cube.row = CubePtr -> r; cube.col = CubePtr -> c;
  2157.             PushCube(cube);
  2158.             return(TRUE);
  2159.            }
  2160.         else
  2161.             return(FALSE);
  2162.        }
  2163.     else
  2164.        {
  2165.         if (TrialW[TrialLen-1] == CubePtr -> val)
  2166.            {
  2167.             cube.row = CubePtr -> r; cube.col = CubePtr -> c;
  2168.             PushCube(cube);
  2169.             CubePtr -> Cused = TRUE;
  2170.             for (Neighbor=0; Neighbor < 8; Neighbor++)
  2171.                {
  2172.                 if (Verify(TrialW, CubePtr -> link[Neighbor], TrialLen-1))
  2173.                    {
  2174.                     CubePtr -> Cused = FALSE;
  2175.                     return(TRUE);
  2176.                    }
  2177.                }
  2178.             CubePtr -> Cused = FALSE;
  2179.             PopCube();
  2180.            }
  2181.         return(FALSE);
  2182.        }
  2183.    }
  2184.  
  2185. /* Score the list of words whose handle is hList. */
  2186. /* Running score and word count are updated */
  2187. /* This routine also updates the window to display the scores */
  2188. /* Return value is the score for the round */
  2189. /* Running score and running word count are updated */
  2190. int Score(hList, RScore, RWords)
  2191. HWND hList;
  2192. int *RScore, *RWords;
  2193.    {
  2194.     char CurLine[30];
  2195.     char OutBuf[30];
  2196.     int CurScore;
  2197.     int count; /* count of lines in the list */
  2198.     int wcount;  /* count of good words */
  2199.     int Tscore;  /* Total score for this round*/
  2200.     int LineNo;
  2201.     HDC hDC;   /* to calclulate width of word */
  2202.     MSG msg;   /* for PeekMessage yield */
  2203.  
  2204.     hDC = GetDC(hList);
  2205.  
  2206.     Tscore = 0;
  2207.     wcount = 0;
  2208.     count = SendMessage(hList, LB_GETCOUNT, 0, 0L);
  2209.     for (LineNo=0; LineNo < count; LineNo++)
  2210.        {
  2211.         SendMessage(hList, LB_GETTEXT, LineNo, (LONG) (LPSTR) CurLine);
  2212.         if (*CurLine >= 'A')
  2213.            {
  2214.             wcount++;
  2215.             switch (strlen(CurLine))        
  2216.                {
  2217.                 case 4:
  2218.                     CurScore=1; break;
  2219.                 case 5:
  2220.                     CurScore=2; break;
  2221.                 case 6:
  2222.                     CurScore=3; break;
  2223.                 case 7:
  2224.                     CurScore=5; break;
  2225.                 default:
  2226.                     CurScore=11; break;
  2227.                }
  2228.  
  2229.             /* Format score for display */
  2230.             if (LOWORD(GetTextExtent(hDC, CurLine, strlen(CurLine))) < TScoreWidth)
  2231.                {
  2232.                 /* the only possible word scores have 1 and 2 digits */
  2233.                 if (CurScore < 10)
  2234.                     sprintf(OutBuf, "%s\t\t\t%d", CurLine, CurScore); /* 1 digit */
  2235.                 else
  2236.                     sprintf(OutBuf, "%s\t\t%d", CurLine, CurScore);   /* 2 digits */
  2237.                }
  2238.             else  /* Word is too wide, do what we can */
  2239.                 sprintf(OutBuf, "%s %d", CurLine, CurScore);
  2240.  
  2241.             SendMessage(hList, LB_DELETESTRING, LineNo, 0L);
  2242.             SendMessage(hList, LB_INSERTSTRING, LineNo, (LONG) (LPSTR) OutBuf);
  2243.             Tscore += CurScore;
  2244.            }
  2245.        }
  2246.     *RScore += Tscore;
  2247.     if (Tscore < 10)
  2248.         sprintf(OutBuf, "Total Score:\t\t\t%d", Tscore);
  2249.     else if (Tscore < 100)
  2250.         sprintf(OutBuf, "Total Score:\t\t%d", Tscore);
  2251.     else
  2252.         /* Assume that there are no score totals of 4 digits */
  2253.         sprintf(OutBuf, "Total Score:\t%d", Tscore);
  2254.  
  2255.     SendMessage(hList, LB_INSERTSTRING, -1, (LONG) (LPSTR) OutBuf);
  2256.  
  2257.     if (wcount != 1)
  2258.         sprintf(OutBuf, "(%d Words)", wcount);
  2259.     else
  2260.         strcpy(OutBuf, "(1 Word)");
  2261.     *RWords += wcount;
  2262.     SendMessage(hList, LB_INSERTSTRING, -1, (LONG) (LPSTR) OutBuf);
  2263.     /* set selection in order to scroll last line of window into view */
  2264.     count = SendMessage(hList, LB_GETCOUNT, 0, 0L);
  2265.     SendMessage(hList, LB_SETCURSEL, count-1, 0L);  /* to get bottom in view */
  2266.     SendMessage(hList, LB_SETCURSEL, count-2, 0L);  /* highlight score line */
  2267.     ReleaseDC(hList, hDC);
  2268.     return(Tscore);
  2269.    }
  2270.  
  2271. /* Process the End-of-game signal */
  2272. ProcessBagoEndGame(hWnd)
  2273. HWND  hWnd;
  2274.    {
  2275.     int Pwcount;        /* how many words the user entered */
  2276.     int Pwi;            /* the index of the word we're looking at */
  2277.     int Pwl;            /* length of the word we're looking at */
  2278.     int Cwcount;        /* same for computer's list */
  2279.     int Cwi;
  2280.     BOOL Bogus;         /* true if any bogus words entered */
  2281.     int RewardLevel;    /* How well the computer was beat */
  2282.     int PicNumber;      /* Resource number of reward picture */
  2283.     int Uscore, Cscore; /* User, Computer score for round */
  2284.     char CurWord[30];   /* a word from User word list */
  2285.     char CurWord1[30];  /* a word from User word list */
  2286.     char RootWord[30];  /* the root of the above (could be equal) */
  2287.     char OutBuf[30];    /* buffer for string output */
  2288.     HANDLE hOldBuffer;  /* The raw buffer from the User Word list */
  2289.     HANDLE hCurWord;    /* Handle, if any to current word in dictionary */
  2290.     BOOL BadWord;       /* Set to true if current word is disqualified */
  2291.     HCURSOR hOldCursor;
  2292.     MSG msg;            /* for PeekMessage Yield */
  2293.  
  2294.     GameOver = TRUE;    /* This global will abort computer word search */
  2295.  
  2296.     /* Sound the bell!  Open and close the sound device every time */
  2297.     /* it doesn't take too much time, and also don't want to hog the */
  2298.     /* sound generator away from other windows */
  2299.     if (Sound)
  2300.        {
  2301.         OpenSound();
  2302.         SetVoiceAccent(1, 120, 50, S_STACCATO, 0);
  2303.         SetVoiceNote(1, 52, 32, 0);
  2304.         SetVoiceNote(1, 48, 32, 0);
  2305.         SetVoiceNote(1, 52, 32, 0);
  2306.         SetVoiceNote(1, 48, 32, 0);
  2307.         SetVoiceNote(1, 52, 32, 0);
  2308.         StartSound();
  2309.        }
  2310.  
  2311.     hOldCursor = SetCursor(hHourGlass);  /* could be lengthy operation */
  2312.  
  2313.     EnableCubes(FALSE); /* Stop player input! */
  2314.     EnableWindow(hUEdit, FALSE);
  2315.  
  2316.     /* Game over, allow dictionary functions again */
  2317.     EnableMenuItem(GetMenu(hWnd), 2, MF_BYPOSITION | MF_ENABLED);
  2318.     DrawMenuBar(hWnd);
  2319.  
  2320.     /* get all the words from the user list and process */
  2321.     Pwcount = SendMessage(hUEdit, EM_GETLINECOUNT, 0, 0L);
  2322.  
  2323.     Bogus = FALSE;
  2324.     /* Capitalize, trim, and copy all words from Edit to List */
  2325.     for (Pwi=0; Pwi<Pwcount; Pwi++)
  2326.        {
  2327.         CurWord[0] = 30;  CurWord[1] = 0;  /* windows wants a max byte count */
  2328.         Pwl = SendMessage(hUEdit, EM_GETLINE, Pwi, (LONG) (LPSTR) CurWord);
  2329.         TrimWord(CurWord, Pwl);  /* make into a valid word */
  2330.         /* For each reasonable word, display in user's buffer, and optionally */
  2331.         /* add it into the dictionary */
  2332.         if (strlen(CurWord) > 3)
  2333.            {    
  2334.             /* Remove suffix twice, for things like JAILERS, PAIRINGS... */
  2335.             RemoveSuffix(CurWord, RootWord);
  2336.             hCurWord = RemoveSuffix(RootWord, RootWord);
  2337.             if (hCurWord)
  2338.             /* if word is known, update frequencies */
  2339.                 IncFreq(hCurWord);
  2340.             else if (Learn && strlen(RootWord) <= 10)
  2341.             /* if in Learn mode, and word is short enough, try to add */
  2342.                {
  2343.                 if (LocalCompact(USERWORDLEN) >= USERWORDLEN)
  2344.                 /* Do not learn words if low on heap space */
  2345.                    {
  2346.                     sprintf(OutBuf, "Add %s to dictionary?", RootWord);
  2347.                     if (IDYES == MessageBox(hWnd, OutBuf, "Learn Mode", MB_YESNO))
  2348.                         /* 0x8000 suffix indicates word is new and should */
  2349.                         /* have a human look it over for suffixes */
  2350.                        {
  2351.                         RemoveQu(RootWord);
  2352.                         AddLeaf(RootWord, &hTreeTop, 1, 0x8000, NULL);
  2353.                         DictChanged = TRUE;
  2354.                        }
  2355.                    }
  2356.                 else
  2357.                    {
  2358.                     MessageBox(hWnd, "Oops, running out of heap space (memory).\n"
  2359.                         "Turning word learning feature off.",
  2360.                         "Warning", MB_OK | MB_ICONASTERISK);
  2361.                     ProcessBagoLearn(hWnd, MN_DD_OPTIONS + MN_OPT_LEARN);
  2362.                    }
  2363.                }
  2364.            }
  2365.         SendMessage(hUList, LB_INSERTSTRING, -1, (LONG) (LPSTR) CurWord);
  2366.        }
  2367.  
  2368.     /* Now all words are copied to player LIST */
  2369.     /* Exchange the windows */
  2370.     ShowWindow(hUList, SW_SHOW);
  2371.     ShowWindow(hUEdit, SW_HIDE);
  2372.  
  2373.     /* If the computer is playing, display it's list */
  2374.     if (CPlay)
  2375.        {
  2376.         /* eliminate flicker */
  2377.         SendMessage(hCList, WM_SETREDRAW, FALSE, 0L);
  2378.         DisplayClist(hCListTop);
  2379.         /* force update */
  2380.         SendMessage(hCList, LB_INSERTSTRING, 0, (LONG) (LPSTR) "");
  2381.         /* turn flicker back on so that scoring will be dramatic */
  2382.         SendMessage(hCList, WM_SETREDRAW, TRUE, 0L);
  2383.         SendMessage(hCList, LB_DELETESTRING, 0, 0L);   
  2384.         Cwcount = SendMessage(hCList, LB_GETCOUNT, 0, 0L);
  2385.        }
  2386.  
  2387.     /* Now start cancelling and disqualifying words */
  2388.     for (Pwi=0; Pwi < Pwcount; Pwi++)
  2389.        {
  2390.         /* select word from player list and get it */
  2391.         SendMessage(hUList, LB_SETCURSEL, Pwi, 0L);
  2392.         SendMessage(hUList, LB_GETTEXT, Pwi, (LONG) (LPSTR) CurWord);
  2393.         if (strlen(CurWord))    /* ignore blank lines */
  2394.            {
  2395.             BadWord = FALSE;
  2396.             if (strlen(CurWord) < 4)
  2397.             /* word too short */
  2398.                {
  2399.                 strcpy(OutBuf, "<");
  2400.                 strcat(OutBuf, CurWord);
  2401.                 SendMessage(hUList, LB_DELETESTRING, Pwi, 0L);
  2402.                 SendMessage(hUList, LB_INSERTSTRING, Pwi, (LONG) (LPSTR) OutBuf);
  2403.                 BadWord = TRUE;
  2404.                }
  2405.             if (!BadWord && CPlay)
  2406.                {
  2407.                 /* see if the word is duplicated in the computer's list */
  2408.                 for (Cwi=0; Cwi < Cwcount; Cwi++)
  2409.                    {
  2410.                     SendMessage(hCList, LB_GETTEXT, Cwi, (LONG) (LPSTR) CurWord1);
  2411.                     if (!strcmp(CurWord, CurWord1))
  2412.                     /* duplicate found between Player and Computer */
  2413.                        {
  2414.                         strcpy(OutBuf, " ");
  2415.                         strcat(OutBuf, CurWord);
  2416.                         SendMessage(hUList, LB_DELETESTRING, Pwi, 0L);
  2417.                         SendMessage(hUList, LB_INSERTSTRING, Pwi, (LONG) (LPSTR) OutBuf);
  2418.                         SendMessage(hCList, LB_DELETESTRING, Cwi, 0L);
  2419.                         SendMessage(hCList, LB_INSERTSTRING, Cwi, (LONG) (LPSTR) OutBuf);
  2420.                         BadWord = TRUE;
  2421.                         break;
  2422.                        }
  2423.                    }
  2424.                }
  2425.             if (!BadWord)
  2426.                {
  2427.                 /* See if player listed this word twice */
  2428.                 /* Tricky, since could match a word already disqualified */
  2429.                 for (Cwi=0; Cwi < Pwi; Cwi++)
  2430.                    {
  2431.                     SendMessage(hUList, LB_GETTEXT, Cwi, (LONG) (LPSTR) CurWord1);
  2432.                     if ((*CurWord1 >= 'A' && !strcmp(CurWord, CurWord1))
  2433.                         || (*CurWord1 < 'A' && !strcmp(CurWord, &CurWord1[1])))
  2434.                     /* word was listed twice - disqualify the second one */
  2435.                        {
  2436.                         strcpy(OutBuf, "\"");
  2437.                         strcat(OutBuf, CurWord);
  2438.                         SendMessage(hUList, LB_DELETESTRING, Pwi, 0L);
  2439.                         SendMessage(hUList, LB_INSERTSTRING, Pwi, (LONG) (LPSTR) OutBuf);
  2440.                         BadWord = TRUE;
  2441.                         break;
  2442.                        }
  2443.                    }
  2444.                }
  2445.             if (!BadWord)
  2446.             /* Make sure the word does appear on the board */
  2447.                {
  2448.                 BOOL found;
  2449.                 int row, col;
  2450.                 
  2451.                 RemoveQu(CurWord);  /* convert Qu to Q for verifying purposes */
  2452.                 found = FALSE;
  2453.                 for (row=0; row<NROWS; row++)
  2454.                    {
  2455.                     for (col=0; col<NCOLS; col++)
  2456.                        {
  2457.                         if (verify(CurWord, &board[row][col], strlen(CurWord)))
  2458.                            {
  2459.                             found = TRUE;
  2460.                             break;
  2461.                            }
  2462.                        }
  2463.                     if (found) break;
  2464.                    }
  2465.                 AddQu(CurWord);  /* put back Qu for display */
  2466.                 if (!found)
  2467.                    {
  2468.                     strcpy(OutBuf, "?");
  2469.                     strcat(OutBuf, CurWord);
  2470.                     SendMessage(hUList, LB_DELETESTRING, Pwi, 0L);
  2471.                     SendMessage(hUList, LB_INSERTSTRING, Pwi, (LONG) (LPSTR) OutBuf);
  2472.                     BadWord = TRUE;
  2473.                    }
  2474.                }
  2475.             if (!BadWord && (!isword(CurWord) || Pwi == Pwcount-1))
  2476.             /* 1. Possibly a partial line is typed as the last entry */
  2477.             /* 2. Somebody trying to enter bogus words */
  2478.                {
  2479.                 /* do we know the word? */
  2480.                 RemoveSuffix(CurWord, OutBuf);  /* remove suffix twice in case */
  2481.                 RemoveSuffix(OutBuf, OutBuf);   /* MAKERS, MAKINGS, etc */
  2482.                 RemoveQu(OutBuf);
  2483.                 if (!FindLeaf(OutBuf, hTreeTop))
  2484.                    {
  2485.                     sprintf(OutBuf, "Is %s really a word?", CurWord);
  2486.                     if (MessageBox(hWnd, OutBuf, "", MB_YESNO) == IDNO)
  2487.                        {
  2488.                         strcpy(OutBuf, "-");
  2489.                         strcat(OutBuf, CurWord);
  2490.                         SendMessage(hUList, LB_DELETESTRING, Pwi, 0L);
  2491.                         SendMessage(hUList, LB_INSERTSTRING, Pwi, (LONG) (LPSTR) OutBuf);
  2492.                         BadWord = TRUE;
  2493.                        }
  2494.                     /* Don't penalize for a badly typed last line */
  2495.                     else if (Pwi != Pwcount-1) Bogus = TRUE;
  2496.                    }
  2497.                }
  2498.            }
  2499.        }
  2500.  
  2501.     /* Show the score and update stats*/
  2502.     NGames++;
  2503.     Uscore = Score(hUList, &RUScore, &RUWords);
  2504.     if (CPlay) Cscore = Score(hCList, &RCScore, &RCWords);
  2505.  
  2506.     /* Set up for the next game */
  2507.     NextRackNumber = rand();
  2508.     SetNextRack(NextRackNumber);
  2509.  
  2510.     SetCursor(hOldCursor);  /* end of lengthy operation */
  2511.  
  2512.     /* Disable stop and enter buttons */
  2513.     EnableWindow(hEnter, FALSE);
  2514.     EnableWindow(hStop, FALSE);
  2515.  
  2516.     /* do the rewards here */
  2517.     if (Uscore > Cscore)
  2518.        {
  2519.         RewardLevel = 0;
  2520.         if (Bogus)
  2521.             RewardLevel = -1;   /* The cheater's reward */
  2522.         else
  2523.            {
  2524.             if (Smartness >= 50 && CountLeaf(hTreeTop) >=400)
  2525.             /* no big rewards if no challenge */
  2526.                {
  2527.                 if (Uscore >= Cscore+5) RewardLevel++;
  2528.                 if (Uscore >= Cscore+10) RewardLevel++;
  2529.                 if (Uscore >= Cscore+15) RewardLevel++;
  2530.                 if (Smartness == 100) RewardLevel++;
  2531.                }
  2532.            }
  2533.         /* Select appropriate reward for winner */
  2534.         switch (RewardLevel)
  2535.            {
  2536.             case -1:
  2537.                 strcpy(OutBuf, "Good!");        /* no reward for cheaters */
  2538.                 PicNumber = 0;
  2539.                 break;
  2540.             case 0:
  2541.             case 1:
  2542.                 strcpy(OutBuf, "Good!");
  2543.                 PicNumber = REWARD1BMP;
  2544.                 break;
  2545.             case 2:
  2546.                 strcpy(OutBuf, "Excellent!");
  2547.                 PicNumber = REWARD2BMP;
  2548.                 break;
  2549.             case 3:
  2550.                 strcpy(OutBuf, "Superb!");
  2551.                 PicNumber = REWARD3BMP;
  2552.                 break;
  2553.             case 4:
  2554.                 strcpy(OutBuf, "Outstanding!");
  2555.                 PicNumber = REWARD4BMP;
  2556.                 break;
  2557.            }
  2558.         if (!Rewards) PicNumber = 0;    /* supress the picture, usually */
  2559.         CreatePicBox(hWnd, PicNumber, OutBuf);
  2560.        }
  2561.     else
  2562.        {
  2563.         /* Set focus to appropriate word list so keyboard-only user can */
  2564.         /* see word paths */
  2565.         /* note: if reward is given, focus is set upon CreatePicBox completion */
  2566.         PostMessage(hWnd, WM_SETFOCUS, 0, 0L);
  2567.        }
  2568.  
  2569.     /* wait for music to complete */
  2570.     if (Sound)
  2571.        {
  2572.         WaitSoundState(S_QUEUEEMPTY);
  2573.         CloseSound();
  2574.        }
  2575.    }
  2576.  
  2577. /* Cleanup when window closed before program termination */
  2578. ProcessBagoClose(hWnd)
  2579. HWND hWnd;
  2580.    {
  2581.     RECT Rect;
  2582.     char ProString[40];
  2583.     BOOL junk;
  2584.  
  2585.     /* Offer to save the dictionary if it changed size */
  2586.     if (DictChanged)
  2587.         if (MessageBox(hWnd, "Dictionary has changed.  Save it?", "Bago", MB_YESNO)==IDYES)
  2588.             ProcessBagoSaveDict(hWnd);
  2589.  
  2590.     /* If a help window is open, kill it */
  2591.     WinHelp(hWnd, BAGOHLP, HELP_QUIT, 0L);
  2592.  
  2593.     /* Save window positions and option settings */
  2594.     if (!IsIconic(hWnd))
  2595.        {
  2596.         /* but don't save ridiculous iconic dimensions */
  2597.         GetWindowRect(hWnd, (LPRECT) &Rect);
  2598.         sprintf(ProString, "%d %d %d %d", Rect.left, Rect.top, Rect.right, Rect.bottom);
  2599.         WritePrivateProfileString("Bago", "Window", ProString, BAGOINI);
  2600.        }
  2601.     sprintf(ProString, "%d", Smartness);
  2602.     WritePrivateProfileString("Bago", "Computer Smartness", ProString, BAGOINI);
  2603.     sprintf(ProString, "%d", EndTime);
  2604.     WritePrivateProfileString("Bago", "Game Duration", ProString, BAGOINI);
  2605.     sprintf(ProString, "%d", Sound);
  2606.     WritePrivateProfileString("Bago", "Sound", ProString, BAGOINI);
  2607.     sprintf(ProString, "%d", UseTimer);
  2608.     WritePrivateProfileString("Bago", "Timed Game", ProString, BAGOINI);
  2609.     sprintf(ProString, "%d", Learn);
  2610.     WritePrivateProfileString("Bago", "Learn Words", ProString, BAGOINI);
  2611.     sprintf(ProString, "%d", CPlay);
  2612.     WritePrivateProfileString("Bago", "Computer Plays", ProString, BAGOINI);
  2613.     sprintf(ProString, "%d", RotCubes);
  2614.     WritePrivateProfileString("Bago", "Rotatable Cubes", ProString, BAGOINI);
  2615.     /* don't want to show string in BAGOINI unless it is enabled */
  2616.     if (Rewards)
  2617.        {
  2618.         WritePrivateProfileString("Bago", "Pictoral", "1", BAGOINI);
  2619.         DeleteObject(hEyes);
  2620.        }
  2621.     else
  2622.         WritePrivateProfileString("Bago", "Pictoral", NULL, BAGOINI);
  2623.  
  2624.     DestroyLeaf(hTreeTop);      /* return all allocated memory */
  2625.  
  2626.     DeleteObject(hGrayBrush0);  /* put away our pens */
  2627.     DeleteObject(hGrayBrush1);
  2628.     DeleteObject(hGrayBrush2);
  2629.    }
  2630.  
  2631. /* Load the dictionary from disk, if any */
  2632. /* return FALSE if dictionary not found */
  2633. BOOL LoadDictionary(hWnd)
  2634.    {
  2635.     int hDictFile;      /* handle of dictionary file */
  2636.     HANDLE hLeafPtr;
  2637.     char linebuf[80];
  2638.     DW TempWord;  /* used to hold a single word at a time from dict */
  2639.     char *bufptr;
  2640.     OFSTRUCT OfStruct;
  2641.     HWND hPro;
  2642.     unsigned charcount;
  2643.     HCURSOR hOldCursor;
  2644.  
  2645.     /* possibly lengthy operation */
  2646.     hOldCursor = SetCursor(hHourGlass);
  2647.     
  2648.     /* Get rid of whatever dictionary was in core */
  2649.     DestroyLeaf(hTreeTop);
  2650.     hTreeTop = NULL;
  2651.  
  2652.     if ((hDictFile = OpenFile(BAGODIC, &OfStruct, OF_READ)) < 0)
  2653.        {
  2654.         /* cannot open file */
  2655.         return(FALSE);
  2656.        }
  2657.     else
  2658.        {
  2659.         hPro = CreateProBox(hWnd);
  2660.         /* wParam is range, lParam is message string */
  2661.         SendMessage(hPro, PRO_INIT, filelength(hDictFile), (LONG) (LPSTR) "Loading Dictionary");
  2662.         charcount = 0;
  2663.  
  2664.         while (!eof(hDictFile))
  2665.            {
  2666.             /* Stop reading if we run out of heap space.  Leave enough for */
  2667.             /* the user's edit window */
  2668.             if (LocalCompact(USERWORDLEN) < USERWORDLEN)
  2669.                {
  2670.                 MessageBox(hWnd, BAGODIC " is too big.  Only part of it was read.",
  2671.                     "Warning", MB_OK | MB_ICONASTERISK | MB_SYSTEMMODAL);
  2672.                 break;
  2673.                }
  2674.  
  2675.             /* Read a line.  There is no formatted read from a file handle */
  2676.             /* Therefore, must build the line ourselves */
  2677.             bufptr = linebuf;
  2678.             while (read(hDictFile, bufptr, 1) > 0)
  2679.                {
  2680.                 charcount++;
  2681.                 if (*bufptr == '\n') break;  /* stop at end of line */
  2682.                 bufptr++;
  2683.                }
  2684.             *(++bufptr) = NULL;  /* terminate the string */
  2685.  
  2686.             if (strlen(linebuf))
  2687.                {
  2688.                 TempWord.Freq = 0;      /* default values in case only a */
  2689.                 TempWord.Suffix1 = 0;   /* word is included on the line */
  2690.                 /* get word and add it to data structure */
  2691.                 sscanf(linebuf, "%s %d %X\r\n",
  2692.                         TempWord.w, &(TempWord.Freq), &(TempWord.Suffix1));
  2693.                 TrimWord(TempWord.w, strlen(TempWord.w));
  2694.                 AddLeaf(TempWord.w, &hTreeTop, TempWord.Freq,
  2695.                         TempWord.Suffix1, NULL);
  2696.                }
  2697.             SendMessage(hPro, PRO_SETPOS, charcount, 0L);
  2698.            }
  2699.         DestroyWindow(hPro);
  2700.         close(hDictFile);
  2701.        }
  2702.  
  2703.     DictChanged = FALSE;
  2704.  
  2705.     SetCursor(hOldCursor);
  2706.     return(TRUE);
  2707.    }
  2708.  
  2709. /* Takes input from dictionary on disk, and eliminates words with frequencies
  2710.  * less than the Fence.  Results written to a different file.
  2711.  */
  2712. BOOL CullDictionary(hWnd, Fence)
  2713. HWND hWnd;
  2714. int Fence;
  2715.    {
  2716.     OFSTRUCT OfStructD;
  2717.     OFSTRUCT OfStructO;
  2718.     int hDictFile;      /* handle of dictionary file */
  2719.     int hOutFile;
  2720.     char linebuf[80];
  2721.     int Freq;
  2722.     char *bufptr;
  2723.     int OverCount, UnderCount;  /* Count of words over and under fence */
  2724.     HWND hPro;
  2725.     unsigned charcount;
  2726.     HCURSOR hOldCursor;
  2727.     
  2728.     /* possibly lengthy operation */
  2729.     hOldCursor = SetCursor(hHourGlass);
  2730.     
  2731.     if ((hDictFile = OpenFile(BAGODIC, &OfStructD,
  2732.         OF_PROMPT | OF_CANCEL | OF_READ)) < 0)
  2733.        {
  2734.         /* cannot open file */
  2735.         return(FALSE);
  2736.        }
  2737.  
  2738.     if ((hOutFile = OpenFile(BAGONEW, &OfStructO,
  2739.         OF_PROMPT | OF_CANCEL | OF_CREATE | OF_WRITE)) < 0)
  2740.        {
  2741.         /* cannot open file */
  2742.         close(hDictFile);
  2743.         return(FALSE);
  2744.        }
  2745.  
  2746.     OverCount = 0;
  2747.     UnderCount = 0;
  2748.     hPro = CreateProBox(hWnd);
  2749.     SendMessage(hPro, PRO_INIT, filelength(hDictFile), (LONG) (LPSTR) "Culling Dictionary");
  2750.     charcount = 0;
  2751.  
  2752.     while (!eof(hDictFile))
  2753.        {
  2754.         /* Read a line.  There is no formatted read from a file handle */
  2755.         /* Therefore, must build the line ourselves */
  2756.         bufptr = linebuf;
  2757.         while (read(hDictFile, bufptr, 1) > 0)
  2758.            {
  2759.             charcount++;
  2760.             if (*bufptr == '\n') break;  /* stop at end of line */
  2761.             bufptr++;
  2762.            }
  2763.         *(++bufptr) = NULL;  /* terminate the string */
  2764.  
  2765.         if (strlen(linebuf))
  2766.            {
  2767.             /* Copy the line only if it has frequency >= fence */
  2768.             Freq = 0;  /* default frequency if none found */
  2769.             sscanf(linebuf, "%*s %d", &Freq);
  2770.             if (Freq >= Fence)
  2771.                {
  2772.                 write(hOutFile, linebuf, strlen(linebuf));
  2773.                 OverCount++;
  2774.                }
  2775.             else
  2776.                {
  2777.                 UnderCount++;
  2778.                }
  2779.            }
  2780.         SendMessage(hPro, PRO_SETPOS, charcount, 0L);
  2781.        }
  2782.     DestroyWindow(hPro);
  2783.     close(hDictFile);
  2784.     close(hOutFile);
  2785.     SetCursor(hOldCursor);
  2786.     sprintf(linebuf, "Total Words: %3d\nKept:        %3d\nDiscarded:   %3d",
  2787.         OverCount+UnderCount, OverCount, UnderCount);
  2788.     MessageBox(hWnd, linebuf, "Results of Culling", MB_OK);
  2789.     return(TRUE);
  2790.    }
  2791.  
  2792. /* This procedure will reset the usage frequencies of a leaf and */
  2793. /* all its inferior leaves to zero */
  2794. ResetFreqLeaf(hLeafPtr)
  2795. HANDLE hLeafPtr;
  2796.    {
  2797.     PDW LeafPtr;
  2798.     
  2799.     if (hLeafPtr)
  2800.        {
  2801.         LeafPtr = (PDW) LocalLock(hLeafPtr);
  2802.         ResetFreqLeaf(LeafPtr -> lt);   /* do all less than */
  2803.         LeafPtr -> Freq = 0;
  2804.         ResetFreqLeaf(LeafPtr -> gt);   /* do all greater than */
  2805.         LocalUnlock(hLeafPtr);
  2806.        }
  2807.    }
  2808.  
  2809. ProcessBagoCreate(hWnd, wParam, lParam)
  2810. HWND     hWnd;
  2811. WORD   wParam;
  2812. LONG   lParam;
  2813.    {
  2814.     HMENU hMenu;                          /* handle to the System menu       */
  2815.     int row, col;  /* used to address board array */
  2816.     HDC hDC;
  2817.  
  2818.     hHourGlass = LoadCursor(NULL, IDC_WAIT);
  2819.     
  2820.     /* Rewards: a semi-hidden menu pick */
  2821.     hMenu = GetSystemMenu(hWnd, FALSE);
  2822.     AppendMenu(hMenu, MF_SEPARATOR, 0, 0L);
  2823.     AppendMenu(hMenu, MF_STRING, MN_REWARDS, " ");
  2824.  
  2825.     /* Load accelerators */
  2826.     hAccTable = LoadAccelerators(hInst, MAKEINTRESOURCE(BAGOACCEL));
  2827.  
  2828.     /* Initialize game variables */
  2829.  
  2830.     /* The contents of the dice, and the linkages between cubes */
  2831.     for (row=0; row < NROWS; row++)
  2832.         for (col=0; col < NCOLS; col++)
  2833.            {
  2834.             board[row][col].val = '@';  /* This is the only time a cube has */
  2835.             board[row][col].orient = 0;  /* this null face */
  2836.             board[row][col].link[0] = (row-1 >= 0 && col-1 >= 0) ? &(board[row-1][col-1]) : NULL;
  2837.             board[row][col].link[1] = (row-1 >= 0) ? &(board[row-1][col]) : NULL;
  2838.             board[row][col].link[2] = (row-1 >= 0 && col+1 < NCOLS) ? &(board[row-1][col+1]) : NULL;
  2839.             board[row][col].link[3] = (col-1 >= 0) ? &(board[row][col-1]) : NULL;
  2840.             board[row][col].link[4] = (col+1 < NCOLS) ? &(board[row][col+1]) : NULL;
  2841.             board[row][col].link[5] = (row+1 < NROWS && col-1 >= 0) ? &(board[row+1][col-1]) : NULL;
  2842.             board[row][col].link[6] = (row+1 < NROWS) ? &(board[row+1][col]) : NULL;
  2843.             board[row][col].link[7] = (row+1 < NROWS && col+1 < NCOLS) ? &(board[row+1][col+1]) : NULL;
  2844.             board[row][col].r = row;
  2845.             board[row][col].c = col;
  2846.             /* now make a corresponding control */
  2847.             board[row][col].hWindow = CreateWindow("CubeButton",
  2848.                 "",
  2849.                 WS_CHILD | WS_DISABLED | WS_CLIPSIBLINGS | WS_VISIBLE,
  2850.                 col*32, row*32, 32, 32,
  2851.                 hWnd,                           /* parent handle             */
  2852.                 ID_FIRSTCUBE + row*NCOLS + col, /* ID is function of position */
  2853.                 hInst,                          /* instance */
  2854.                 NULL);                          /* additional info */
  2855.             if (!board[row][col].hWindow)
  2856.                {
  2857.                 MessageBox(hWnd, "Unable to create cube controls", "BAD ERROR",
  2858.                     MB_ICONQUESTION | MB_OK);
  2859.                 break;
  2860.                }
  2861.            }
  2862.  
  2863.     hTreeTop = NULL;  /* There is no dictionary */
  2864.     hCListTop = NULL;  /* The computer has not found any words */
  2865.  
  2866.     RackNumber = -1;   /* No rack has been assigned, yet */
  2867.     /* Set up the first rack (it is not shown until first NewGame() */
  2868.     srand(GetCurrentTime());
  2869.     NextRackNumber = rand();
  2870.     SetNextRack(NextRackNumber);
  2871.  
  2872.     /* Make controls for ENTER and STOP */
  2873.     hEnter = CreateWindow("Button",
  2874.         NULL,
  2875.         WS_CHILD | WS_DISABLED | WS_CLIPSIBLINGS | WS_VISIBLE
  2876.             | BS_OWNERDRAW | BS_PUSHBUTTON,
  2877.         176, 64, 64, 32,
  2878.         hWnd,
  2879.         ID_ENTER,
  2880.         hInst,
  2881.         NULL);
  2882.  
  2883.     hStop = CreateWindow("Button",
  2884.         NULL,
  2885.         WS_CHILD | WS_DISABLED | WS_CLIPSIBLINGS | WS_VISIBLE
  2886.             | BS_OWNERDRAW | BS_PUSHBUTTON,
  2887.         208, 112, 32, 32,
  2888.         hWnd,
  2889.         ID_STOP,
  2890.         hInst,
  2891.         NULL);
  2892.  
  2893.     if (!hStop || !hEnter)
  2894.         MessageBox(hWnd, "Unable to create cube controls", "BAD ERROR",
  2895.             MB_ICONQUESTION | MB_OK);
  2896.  
  2897.     /* Make the windows for user's word input */
  2898.     /* Same location, but only one appears at a time */
  2899.     hUEdit = CreateWindow("Edit",               /* predefined EDIT type */
  2900.         "",                                     /* initial text for EDIT */
  2901.         WS_CHILD | ES_MULTILINE | ES_UPPERCASE | WS_BORDER | WS_CLIPSIBLINGS
  2902.             | WS_VSCROLL | ES_AUTOVSCROLL,  /* window style    */
  2903.         0, 0, 0, 0,                     /* leave sizing to WM_SIZE */
  2904.         hWnd,                           /* parent handle             */
  2905.         ID_USERWORDS,                   /* menu or child ID          */
  2906.         hInst,                          /* instance                  */
  2907.         NULL);                          /* additional info           */
  2908.  
  2909.     hUList = CreateWindow("Listbox",    /* Predefined type LISTBOX */
  2910.         "",                             /* No title */
  2911.         WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS | LBS_USETABSTOPS | LBS_NOTIFY
  2912.             | LBS_NOINTEGRALHEIGHT | WS_VSCROLL,       /* window style */
  2913.         0, 0, 0, 0,                     /* leave sizing to WM_SIZE */
  2914.         hWnd,                           /* parent handle             */
  2915.         ID_PLIST,                       /* menu or child ID          */
  2916.         hInst,                          /* instance                  */
  2917.         NULL);                          /* additional info           */
  2918.  
  2919.     if (!hUList || !hUEdit)
  2920.         MessageBox(hWnd, "Unable to create window for player's words", "BAD ERROR",
  2921.             MB_ICONQUESTION | MB_OK);
  2922.  
  2923.     /* compute size and tab stops for list boxes */
  2924.     hDC = GetDC(hWnd);
  2925.     TabStops[0] = TScoreWidth * 4 / LOWORD(GetDialogBaseUnits());
  2926.     TabStops[1] = (TScoreWidth + LOWORD(GetTextExtent(hDC, "0", 1)))
  2927.                         * 4 / LOWORD(GetDialogBaseUnits());
  2928.     TabStops[2] = (TScoreWidth + LOWORD(GetTextExtent(hDC, "00", 2)))
  2929.                         * 4 / LOWORD(GetDialogBaseUnits());
  2930.     ReleaseDC(hWnd, hDC);
  2931.  
  2932.     /* Do the options */
  2933.     Smartness = GetPrivateProfileInt("Bago", "Computer Smartness", 15, BAGOINI);
  2934.     EndTime   = GetPrivateProfileInt("Bago", "Game Duration", 180, BAGOINI);
  2935.     Sound     = GetPrivateProfileInt("Bago", "Sound", TRUE, BAGOINI);
  2936.     UseTimer  = GetPrivateProfileInt("Bago", "Timed Game", TRUE, BAGOINI);
  2937.     Learn     = GetPrivateProfileInt("Bago", "Learn Words", FALSE, BAGOINI);
  2938.     CPlay     = GetPrivateProfileInt("Bago", "Computer Plays", TRUE, BAGOINI);
  2939.     RotCubes  = GetPrivateProfileInt("Bago", "Rotatable Cubes", FALSE, BAGOINI);
  2940.     Rewards   = GetPrivateProfileInt("Bago", "Pictoral", FALSE, BAGOINI);
  2941.  
  2942.     if (UseTimer)
  2943.        {
  2944.         UseTimer = FALSE;  /* set up for routine which toggles */
  2945.         ProcessBagoTimer(hWnd, MN_DD_OPTIONS + MN_OPT_TIMER);
  2946.        }
  2947.  
  2948.     if (CPlay)
  2949.        {
  2950.         CPlay = FALSE;  /* set up for routine which toggles */
  2951.         ProcessBagoCPlay(hWnd, MN_DD_OPTIONS + MN_OPT_CPLAY);
  2952.        }
  2953.  
  2954.     if (Learn)
  2955.        {
  2956.         Learn = FALSE;  /* set up for routine which toggles */
  2957.         ProcessBagoLearn(hWnd, MN_DD_OPTIONS + MN_OPT_LEARN);
  2958.        }
  2959.  
  2960.     if (RotCubes)
  2961.        {
  2962.         RotCubes = FALSE;  /* set up for routine which toggles */
  2963.         ProcessBagoRotCubes(hWnd, MN_DD_OPTIONS + MN_OPT_ROTATE);
  2964.        }
  2965.  
  2966.     if (Sound)
  2967.        {
  2968.         Sound = FALSE;  /* set up for routine which toggles */
  2969.         ProcessBagoSound(hWnd, MN_DD_OPTIONS + MN_OPT_SOUND);
  2970.        }
  2971.  
  2972.     if (Rewards)
  2973.        {
  2974.         Rewards = FALSE;  /* set up for routine which toggles */
  2975.         ProcessBagoRewards(hWnd, MN_DD_OPTIONS + MN_OPT_REWARDS);
  2976.        }
  2977.  
  2978.     /* Tell the User and computer lists where to put tabs */
  2979.     PostMessage(hUList, LB_SETTABSTOPS, 3, (LONG)(LPSTR) TabStops);
  2980.  
  2981.     /* Post message to start the rest of the initialization */
  2982.     /* But first, we want to allow the window to be shown */
  2983.     PostMessage(hWnd, BAGOM_INIT, 0, 0L);
  2984.    }
  2985.  
  2986. /* Load a rack from a file.  The first 25 alphabetic characters */
  2987. /* in the file are used.  Note that this loads the NEXT rack, not the */
  2988. /* actual game board that is being displayed */
  2989. ProcessBagoGLoad(hWnd)
  2990. HWND hWnd;
  2991.    {
  2992.     int hInFile;
  2993.     char InBuf[30];
  2994.     OFSTRUCT OfStruct;
  2995.     char *BufPtr;
  2996.     int row, col;       /* used to step through board array */
  2997.     char InChar;        /* We input characters one at a time */
  2998.     int nchar;          /* How many valid characters we have read */
  2999.     char FileName[30];
  3000.     int dummy;
  3001.  
  3002.     strcpy(FileName, BAGOSAVE);
  3003.     if (InputBox(hWnd, "Load rack from:",
  3004.         FileName, &dummy, FALSE) == ID_CANCEL) return;
  3005.     
  3006.     if ((hInFile = OpenFile(FileName, &OfStruct,
  3007.         OF_PROMPT | OF_CANCEL | OF_READ)) >= 0)
  3008.        {
  3009.         nchar = 0;
  3010.         BufPtr = InBuf;
  3011.         while (nchar < NDICE && read(hInFile, &InChar, 1))
  3012.            {
  3013.             if (isupper(*BufPtr = toupper(InChar)))
  3014.                {
  3015.                 BufPtr++;
  3016.                 nchar++;
  3017.                }
  3018.            }
  3019.         close(hInFile);
  3020.  
  3021.         if (nchar == NDICE)
  3022.            {
  3023.             BufPtr = InBuf;
  3024.             for (row=0; row < NROWS; row++)
  3025.                {
  3026.                 for (col=0; col < NCOLS; col++)
  3027.                    {
  3028.                     NextVal[row][col] = *BufPtr;
  3029.                     BufPtr++;
  3030.                    }
  3031.                }
  3032.            }
  3033.         else
  3034.            {
  3035.             MessageBox(hWnd, "Not enough letters in file.", "Error",
  3036.             MB_ICONQUESTION | MB_OK);
  3037.            }
  3038.  
  3039.         NextRackNumber = -1;        /* we have no idea what rack number is, now */
  3040.        }
  3041.    }
  3042.  
  3043. /* Save the current rack into a file */
  3044. /* Also save's both the user's and computer's word lists, if any */
  3045. ProcessBagoGSave(hWnd)
  3046. HWND hWnd;
  3047.    {
  3048.     int hOutFile;
  3049.     char OutBuf[40];
  3050.     OFSTRUCT OfStruct;
  3051.     char *BufPtr;
  3052.     int row, col;  /* used to step through board array */
  3053.     int nlines;    /* for writing out list box */
  3054.     char FileName[30];
  3055.     int dummy;
  3056.  
  3057.     strcpy(FileName, BAGOSAVE);
  3058.     if (InputBox(hWnd, "Save this rack to:",
  3059.         FileName, &dummy, FALSE) == ID_CANCEL) return;
  3060.     
  3061.     if ((hOutFile = OpenFile(FileName, &OfStruct,
  3062.         OF_PROMPT | OF_CANCEL | OF_CREATE | OF_WRITE)) >= 0)
  3063.        {
  3064.         BufPtr = OutBuf;
  3065.         for (row=0; row < NROWS; row++)
  3066.            {
  3067.             for (col=0; col < NCOLS; col++)
  3068.                {
  3069.                 *BufPtr = board[row][col].val;
  3070.                 BufPtr++;
  3071.                }
  3072.             *BufPtr = '\r'; BufPtr++;
  3073.             *BufPtr = '\n'; BufPtr++;
  3074.            }
  3075.         *BufPtr = NULL; /* terminating null */
  3076.         write(hOutFile, OutBuf, strlen(OutBuf));
  3077.  
  3078.         /* Copy user word list, if any */
  3079.         if (hUList)
  3080.            {
  3081.             strcpy(OutBuf, "\r\nYour Words ----\r\n");
  3082.             write(hOutFile, OutBuf, strlen(OutBuf));
  3083.             nlines = SendMessage(hUList, LB_GETCOUNT, 0, 0L);
  3084.             for (row=0; row < nlines; row++)
  3085.                {
  3086.                 SendMessage(hUList, LB_GETTEXT, row, (LONG)(LPSTR) OutBuf);
  3087.                 strcat(OutBuf, "\r\n");
  3088.                 write(hOutFile, OutBuf, strlen(OutBuf));
  3089.                }
  3090.            }
  3091.  
  3092.         /* Copy computer word list, if any */
  3093.         if (hCList)
  3094.            {
  3095.             strcpy(OutBuf, "\r\nBago's Words ----\r\n");
  3096.             write(hOutFile, OutBuf, strlen(OutBuf));
  3097.             nlines = SendMessage(hCList, LB_GETCOUNT, 0, 0L);
  3098.             for (row=0; row < nlines; row++)
  3099.                {
  3100.                 SendMessage(hCList, LB_GETTEXT, row, (LONG) (LPSTR) OutBuf);
  3101.                 strcat(OutBuf, "\r\n");
  3102.                 write(hOutFile, OutBuf, strlen(OutBuf));
  3103.                }
  3104.            }
  3105.         
  3106.         close(hOutFile);
  3107.        }
  3108.    }
  3109.  
  3110. /* Enable/disable feature of learning new words from user's play */
  3111. ProcessBagoLearn(hWnd, wParam)
  3112. HWND hWnd;
  3113. WORD wParam;
  3114.    {
  3115.     if (!Learn)
  3116.     /* Not in learn mode, enable */
  3117.        {
  3118.         CheckMenuItem(GetMenu(hWnd), wParam, MF_BYCOMMAND | MF_CHECKED);
  3119.         Learn = TRUE;
  3120.        }
  3121.     else
  3122.     /* In learn mode now, disable */
  3123.        {
  3124.         CheckMenuItem(GetMenu(hWnd), wParam, MF_BYCOMMAND | MF_UNCHECKED);
  3125.         Learn = FALSE;
  3126.        }
  3127.    }
  3128.  
  3129. /* Enable or disable the egg timer option.  Check on the menu item is updated */
  3130. ProcessBagoTimer(hWnd, wParam)
  3131. HWND hWnd;
  3132. WORD wParam;
  3133.    {
  3134.     if (!UseTimer)
  3135.     /* Egg timer does not exist, create it. */
  3136.        {
  3137.         CheckMenuItem(GetMenu(hWnd), wParam, MF_BYCOMMAND | MF_CHECKED);
  3138.         hEgg = CreateEggTimer(hWnd);
  3139.         UseTimer = TRUE;
  3140.        }
  3141.     else
  3142.        {
  3143.         CheckMenuItem(GetMenu(hWnd), wParam, MF_BYCOMMAND | MF_UNCHECKED);
  3144.         DestroyWindow(hEgg);
  3145.         hEgg = NULL;
  3146.         UseTimer = FALSE;
  3147.        }
  3148.    }
  3149.  
  3150. /* The Computer play menu selection enables/disables computer from playing */
  3151. ProcessBagoCPlay(hWnd, wParam)
  3152. HWND hWnd;
  3153. WORD wParam;
  3154.    {
  3155.     HMENU hMenu;
  3156.  
  3157.     hMenu = GetMenu(hWnd);
  3158.  
  3159.     if (!CPlay)
  3160.     /* If not in computer play, make the window */
  3161.        {
  3162.         RECT Rect;
  3163.         
  3164.         CheckMenuItem(hMenu, wParam, MF_BYCOMMAND | MF_CHECKED);
  3165.  
  3166.         hCList = CreateWindow("Listbox",        /* Predefined type LISTBOX */
  3167.                 "",                             /* No title */
  3168.                 WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPSIBLINGS | LBS_NOTIFY
  3169.                     | LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL,
  3170.                 0, 0, 0, 0,                     /* let WM_SIZE handle */
  3171.                 hWnd,                           /* parent handle           */
  3172.                 ID_CPLAY,                       /* menu or child ID        */
  3173.                 hInst,                          /* instance                */
  3174.                 NULL);                          /* additional info         */
  3175.  
  3176.         if (!hCList)
  3177.            {
  3178.             MessageBox(hWnd, "Unable to create Computer's window", "ERROR",
  3179.                 MB_ICONQUESTION | MB_OK);
  3180.            }
  3181.         else
  3182.            {
  3183.             /* so initial size will be set */
  3184.             GetClientRect(hWnd, (LPRECT) &Rect);
  3185.             SendMessage(hWnd, WM_SIZE, SIZENORMAL,
  3186.                 MAKELONG(Rect.right-Rect.left, Rect.bottom-Rect.top));
  3187.             PostMessage(hCList, LB_SETTABSTOPS, 3, (LONG)(LPSTR) TabStops);
  3188.             CPlay = TRUE;
  3189.            }
  3190.        }
  3191.     else
  3192.     /* Turn off Computer Play */
  3193.        {
  3194.         CheckMenuItem(hMenu, wParam, MF_BYCOMMAND | MF_UNCHECKED);
  3195.  
  3196.         DestroyWindow(hCList);  hCList = NULL;
  3197.  
  3198.         CPlay = FALSE;
  3199.        }
  3200.    }
  3201.  
  3202. /* Generate statistics display */
  3203. ProcessBagoStats(hWnd)
  3204. HWND hWnd;
  3205.    {
  3206.     char outstring[200];
  3207.     char compstring[50];
  3208.     int RS;     /* Recommended smartness level */
  3209.     
  3210.     strcpy(outstring, "\t\tYou\t\Computer\n\n");
  3211.     sprintf(compstring, "Total Score:\t%d\t%d\n", RUScore, RCScore);
  3212.     strcat(outstring, compstring);
  3213.     sprintf(compstring, "Total Words:\t%d\t%d\n", RUWords, RCWords);
  3214.     strcat(outstring, compstring);
  3215.     if (NGames > 0)
  3216.        {
  3217.         sprintf(compstring, "Words/Game:\t%d\t%d\n", RUWords/NGames, RCWords/NGames);
  3218.         strcat(outstring, compstring);
  3219.        }
  3220.     if (NGames != 1)
  3221.         sprintf(compstring, "\n%d Rounds Played.\n", NGames);
  3222.     else
  3223.         strcpy(compstring, "\n1 Round Played.\n");
  3224.     strcat(outstring, compstring);
  3225.     if (NGames > 0)
  3226.        {
  3227.         RS = (RCScore > 0) ? (1.0 * RUScore * Smartness) / RCScore : 100;
  3228.         if (RS > 100) RS = 100;
  3229.         sprintf(compstring, "Difficulty = %d; try %d for challenge.\n", Smartness, RS);
  3230.         strcat(outstring, compstring);
  3231.        }
  3232.     else
  3233.        {
  3234.         sprintf(compstring, "Difficulty = %d.\n", Smartness);
  3235.         strcat(outstring, compstring);
  3236.        }
  3237.     MessageBox(hWnd, outstring, "Statistics", MB_OK);
  3238.    }
  3239.  
  3240. /* Enable/disable feature of realistically rotated cubes */
  3241. ProcessBagoRotCubes(hWnd, wParam)
  3242. HWND hWnd;
  3243. WORD wParam;
  3244.    {
  3245.     HMENU hMenu;
  3246.     int row, col;
  3247.  
  3248.     hMenu = GetMenu(hWnd);
  3249.     if (!RotCubes)
  3250.     /* Not in RotCube mode, enable */
  3251.        {
  3252.         CheckMenuItem(hMenu, wParam, MF_BYCOMMAND | MF_CHECKED);
  3253.         RotCubes = TRUE;
  3254.        }
  3255.     else
  3256.     /* In RotCube mode now, disable */
  3257.        {
  3258.         CheckMenuItem(hMenu, wParam, MF_BYCOMMAND | MF_UNCHECKED);
  3259.         RotCubes = FALSE;
  3260.        }
  3261.  
  3262.     /* Need to redisplay all the cubes */
  3263.     for (row=0; row<NROWS; row++)
  3264.         for (col=0; col<NCOLS; col++)
  3265.             InvalidateRect(board[row][col].hWindow, NULL, FALSE);
  3266.    }
  3267.  
  3268. /* Enable/disable feature of pictoral rewards */
  3269. ProcessBagoRewards(hWnd, wParam)
  3270. HWND hWnd;
  3271. WORD wParam;
  3272.    {
  3273.     HMENU hMenu;
  3274.  
  3275.     hMenu = GetSystemMenu(hWnd, FALSE);
  3276.  
  3277.     if (!Rewards)
  3278.     /* Not in Reward mode, enable */
  3279.        {
  3280.         hEyes = LoadBitmap(hInst, MAKEINTRESOURCE(EYESBMP));
  3281.         ModifyMenu(hMenu, MN_REWARDS,
  3282.             MF_BYCOMMAND | MF_BITMAP, MN_REWARDS,
  3283.             (LPSTR) (LONG) hEyes);
  3284.         Rewards = TRUE;
  3285.        }
  3286.     else
  3287.     /* In Reward mode now, disable */
  3288.        {
  3289.         ModifyMenu(hMenu, MN_REWARDS,
  3290.             MF_BYCOMMAND | MF_STRING, MN_REWARDS,
  3291.             "");
  3292.         DeleteObject(hEyes);
  3293.         Rewards = FALSE;
  3294.        }
  3295.    }
  3296.  
  3297. /* Enable/disable sound */
  3298. ProcessBagoSound(hWnd, wParam)
  3299. HWND hWnd;
  3300. WORD wParam;
  3301.    {
  3302.     HMENU hMenu;
  3303.  
  3304.     hMenu = GetMenu(hWnd);
  3305.     if (!Sound)
  3306.     /* Sound off, enable */
  3307.        {
  3308.         CheckMenuItem(hMenu, wParam, MF_BYCOMMAND | MF_CHECKED);
  3309.         Sound = TRUE;
  3310.        }
  3311.     else
  3312.     /* Sound on, disable */
  3313.        {
  3314.         CheckMenuItem(hMenu, wParam, MF_BYCOMMAND | MF_UNCHECKED);
  3315.         Sound = FALSE;
  3316.        }
  3317.    }
  3318.  
  3319. /* saves the entire dictionary to disk */
  3320. ProcessBagoSaveDict(hWnd)
  3321. HWND hWnd;
  3322.    {
  3323.     OFSTRUCT OfStruct;
  3324.     int hDictFile;  /* handle of dictionary file */
  3325.     HCURSOR hOldCursor;
  3326.     
  3327.     if ((hDictFile = OpenFile(BAGODIC, &OfStruct,
  3328.                 OF_PROMPT | OF_CANCEL | OF_CREATE | OF_WRITE)) < 0)
  3329.        {
  3330.         MessageBox(hWnd, "Cannot open dictionary file for writing.", "ERROR",
  3331.             MB_ICONQUESTION | MB_OK);
  3332.        }
  3333.     else
  3334.        {
  3335.         /* show hourglass for long file I/O */
  3336.         hOldCursor = SetCursor(hHourGlass);
  3337.         if (!PrintDict(hDictFile, hTreeTop))
  3338.             MessageBox(hWnd, "Error writing dictionary file.", "ERROR",
  3339.                 MB_ICONQUESTION | MB_OK | MB_SYSTEMMODAL);
  3340.         close(hDictFile);
  3341.  
  3342.         DictChanged = FALSE;
  3343.  
  3344.         SetCursor(hOldCursor);
  3345.        }
  3346.    }
  3347.  
  3348. /* Puts the entire dictionary into the user window */
  3349. ProcessBagoShowDict(hWnd)
  3350. HWND hWnd;
  3351.    {
  3352.     HANDLE hOldBuffer, hNewBuffer;
  3353.     char *pNewBuffer;
  3354.     
  3355.     SendMessage(hCList, WM_SETREDRAW, FALSE, 0L);
  3356.     PrintLeaf(hCList, hTreeTop);
  3357.     SendMessage(hCList, LB_INSERTSTRING, 0, (LONG) (LPSTR) "");
  3358.     SendMessage(hCList, WM_SETREDRAW, TRUE, 0L);
  3359.     SendMessage(hCList, LB_DELETESTRING, 0, 0L);   
  3360.    }
  3361.  
  3362. /* Optimize the dictionary */
  3363. /* The progress box is brought up to inform the user of our progress */
  3364. ProcessBagoOptDict(hWnd)
  3365. HWND hWnd;
  3366.    {
  3367.     HANDLE hFTreeTop;  /* handle to top of frequency-sorted tree */
  3368.     HANDLE hLinArray;  /* handle to sorted linear array of HANDLES */
  3369.     LPHANDLE LinArray;  /* sorted linear array of HANDLES */
  3370.     int nWords;        /* count of number of words in tree */
  3371.     int i;             /* used to index LinArray */
  3372.     LPDW pLinArray;  /* used to retrieve data from LinArray */
  3373.     int first, last;  /* array bounds of a set with equal frequency */
  3374.     int BaseFreq;         /* the frequency of the set above */
  3375.     int CurFreq;        /* frequency of word under inspection */
  3376.     HCURSOR hOldCursor;
  3377.     HWND hPro;
  3378.  
  3379.     OptimizeFail = FALSE;
  3380.     hFTreeTop = NULL;
  3381.     hOldCursor = SetCursor(hHourGlass);
  3382.     nWords = CountLeaf(hTreeTop);
  3383.     hPro = CreateProBox(hWnd);
  3384.     SendMessage(hPro, PRO_INIT, nWords, (LONG) (LPSTR) "Re-sorting Dictionary");
  3385.     i=0;
  3386.     ReSortLeaf(hTreeTop, (LPHANDLE) &hFTreeTop, &i, hPro);
  3387.  
  3388.     /* allocate enough space for linear array */
  3389.     if (!OptimizeFail)
  3390.        {
  3391.         hLinArray = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
  3392.         (LONG) nWords*sizeof(HANDLE));
  3393.         if (!hLinArray) OptimizeFail = TRUE;
  3394.        }
  3395.  
  3396.     if (!OptimizeFail)
  3397.        {
  3398.         LinArray = (LPHANDLE) GlobalLock(hLinArray);
  3399.  
  3400.         /* Get rid of old tree to free up space */
  3401.         DestroyLeaf(hTreeTop);
  3402.         hTreeTop = NULL;
  3403.  
  3404.         /* copy the F-tree into array */
  3405.         i = 0;
  3406.         CopyTreeToLin(hFTreeTop, LinArray, &i);
  3407.  
  3408.         /* Scan the linear array for sets of words with the same frequency */
  3409.         /* and output a balanced tree for each set */
  3410.         SendMessage(hPro, PRO_INIT, nWords, (LONG) (LPSTR) "Balancing Dictionary Tree");
  3411.         i=0;
  3412.         while (i < nWords)
  3413.            {
  3414.             first = i;
  3415.             pLinArray = (LPDW) GlobalLock(LinArray[i]);
  3416.             BaseFreq = pLinArray -> Freq;
  3417.             LocalUnlock(LinArray[i]);
  3418.             do {
  3419.                 i++;
  3420.                 if (i < nWords)
  3421.                    {
  3422.                     pLinArray = (LPDW) GlobalLock(LinArray[i]);
  3423.                     CurFreq = pLinArray -> Freq;
  3424.                     GlobalUnlock(LinArray[i]);
  3425.                    }
  3426.                 SendMessage(hPro, PRO_SETPOS, i, 0L);
  3427.                } while (i < nWords && CurFreq == BaseFreq);
  3428.             last = i - 1;
  3429.             Balance(&hTreeTop, LinArray, first, last);
  3430.            }
  3431.  
  3432.         DestroyWindow(hPro);
  3433.  
  3434.         if (MessageBox(hWnd, "Save optimized dictionary to disk?", "Optimize", MB_YESNO) == IDYES)
  3435.             ProcessBagoSaveDict(hWnd);
  3436.  
  3437.         /* Get rid of global memory used for sort */
  3438.         GlobalUnlock(hLinArray);
  3439.         GlobalFree(hLinArray);
  3440.        }
  3441.     else
  3442.     /* OptimizeFail: not enough global memory */
  3443.        {
  3444.         /* print warning message */
  3445.         MessageBox(hWnd, "Out of memory - can't optimize.", "ERROR",
  3446.             MB_ICONQUESTION | MB_OK);
  3447.  
  3448.         DestroyWindow(hPro);
  3449.         }
  3450.  
  3451.     /* This must be done whether or not GlobalAlloc failed */
  3452.     DestroyGLeaf(hFTreeTop);
  3453.  
  3454.     SetCursor(hOldCursor);
  3455.    }
  3456.  
  3457. /****************************************************************************
  3458.  
  3459.     FUNCTION: About(HWND, unsigned, WORD, LONG)
  3460.  
  3461.     PURPOSE:  Processes messages for "About" dialog box
  3462.  
  3463.     MESSAGES:
  3464.  
  3465.         WM_INITDIALOG - initialize dialog box
  3466.         WM_COMMAND    - Input received
  3467.  
  3468.     COMMENTS:
  3469.  
  3470.         No initialization is needed for this particular dialog box, but TRUE
  3471.         must be returned to Windows.
  3472.  
  3473.         Wait for user to click on "Ok" button, then close the dialog box.
  3474.  
  3475. ****************************************************************************/
  3476.  
  3477. BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
  3478. HWND hDlg;
  3479. unsigned message;
  3480. WORD wParam;
  3481. LONG lParam;
  3482. {
  3483.     switch (message) {
  3484.         case WM_INITDIALOG:
  3485.             return (TRUE);              /* returning TRUE sets focus to OK button */
  3486.  
  3487.         case WM_COMMAND:
  3488.             if (wParam == IDOK) {             /* "OK" box selected?          */
  3489.                 EndDialog(hDlg, NULL);        /* Exits the dialog box        */
  3490.                 return (TRUE);
  3491.             }
  3492.             break;
  3493.     }
  3494.     return (FALSE);                           /* Didn't process a message    */
  3495. }
  3496.  
  3497. /****************************************************************************
  3498.  
  3499.     FUNCTION: EditWord(HWND, unsigned, WORD, LONG)
  3500.  
  3501.     PURPOSE:  Processes messages for "EditWord" dialog box
  3502.  
  3503.     MESSAGES:
  3504.  
  3505.         WM_INITDIALOG - initialize dialog box
  3506.         WM_COMMAND    - Input received
  3507.  
  3508.     COMMENTS:
  3509.  
  3510.         No initialization is needed for this particular dialog box, but TRUE
  3511.         must be returned to Windows.
  3512.  
  3513.         Wait for user to click on "Ok" button, then close the dialog box.
  3514.  
  3515. ****************************************************************************/
  3516.  
  3517. BOOL FAR PASCAL EditWord(hDlg, message, wParam, lParam)
  3518. HWND hDlg;
  3519. unsigned message;
  3520. WORD wParam;
  3521. LONG lParam;
  3522.    {
  3523.     HWND hControl;  /* handle to a control */
  3524.     
  3525.     switch (message) {
  3526.         case WM_INITDIALOG:             /* message: initialize dialog box */
  3527.             hWord = hTreeTop;           /* good a starting point as any */
  3528.             UpdateControls(hDlg);
  3529.             return (FALSE);     /* so windows won't arbitrarily set focus */
  3530.             break;
  3531.         case WM_COMMAND:                /* message: received a command */
  3532.             ProcessEditWordCommand(hDlg, wParam, lParam);
  3533.             return (TRUE);
  3534.             break;
  3535.         default:
  3536.             return (FALSE);                   /* Didn't process a message    */
  3537.     }
  3538. }
  3539.  
  3540. /* returns the Suffix mask, set according to the current buttons */
  3541. /* This automatically returns the virgin bit (MSB) set to 0, since */
  3542. /* ID_LASTSUF - ID_FIRSTSUF does not do all 16 bits */
  3543. unsigned GetButtons(hDlg)
  3544. HWND hDlg;
  3545.    {
  3546.     int nControl;
  3547.     unsigned BitMask;
  3548.     
  3549.     BitMask = 0;
  3550.     
  3551.     for (nControl=ID_LASTSUF; nControl >= ID_FIRSTSUF; nControl--)
  3552.        {
  3553.         BitMask <<= 1;  /* move to the next bit */
  3554.         if (IsDlgButtonChecked(hDlg, nControl)) BitMask |= 1;
  3555.        }
  3556.  
  3557.     return(BitMask);
  3558.    }
  3559.  
  3560. /* Posts the frequency and suffix information from the dialog box to the
  3561.  * dictionary.  The word is assumed to be in global handle hWord.
  3562.  * Returns TRUE if hWord is NULL, or the posting succeneds.
  3563.  * Returns FALSE if the posting fails.  Only possible reason for failure
  3564.  * at present is invalid frequency.
  3565.  */
  3566. BOOL PostWord(hDlg)
  3567.    {
  3568.     PDW pWord;  /* pointer to record corresponding to current word */
  3569.     BOOL TransOk;       /* flag to see if integer translation succeeded */
  3570.     int TempFreq;       /* frequency in the dialog box, if any */
  3571.     unsigned TempSuffix;
  3572.  
  3573.     if (hWord)
  3574.        {
  3575.         TempFreq = GetDlgItemInt(hDlg, ID_FREQ, (BOOL FAR *)&TransOk, FALSE);
  3576.         if (TransOk)
  3577.            {
  3578.             /* rewrite an existing leaf */
  3579.             pWord = (PDW) LocalLock(hWord);
  3580.             /* pWord -> w should already be right */
  3581.             pWord -> Freq = TempFreq;
  3582.             /* user may have changed buttons, so get latest settings */
  3583.             TempSuffix = GetButtons(hDlg);
  3584.             /* note that GetButtons() by nature kills the virgin bit */
  3585.             if (pWord -> Suffix1 != TempSuffix)
  3586.                {
  3587.                 pWord -> Suffix1 = TempSuffix;
  3588.                 DictChanged = TRUE;
  3589.                }
  3590.             LocalUnlock(hWord);
  3591.             return(TRUE);
  3592.            }
  3593.         else
  3594.            {
  3595.             MessageBox(hDlg, "Need non-negative number for word frequency", "Error",
  3596.                 MB_OK);
  3597.             /* restore word in case it was altered */
  3598.             pWord = (PDW) LocalLock(hWord);
  3599.             SetDlgItemText(hDlg, ID_WORD, (LPSTR) pWord -> w);
  3600.             LocalUnlock(hWord);
  3601.             /* Give the focus to the frequency */
  3602.             SetFocus(GetDlgItem(hDlg, ID_FREQ));
  3603.             return(FALSE);
  3604.            }
  3605.        }
  3606.     else
  3607.         return(TRUE);
  3608.    }
  3609.  
  3610. /* The processing of the dialog box for the dictionary editor is complex */
  3611. /* Processing of ID_OK:
  3612.  * 1.  Try to put all the current information back into the dictionary.
  3613.  * 2.  If the word has been changed, try to look it up in the dictionary.
  3614.  *   a.  If the word exists, display the information.
  3615.  *   b.  If the word is NULL, disable most of the editing fields in box.
  3616.  *   c.  If the word is not in the dictionary, see if the user wants to add it.
  3617.  * Processing of ID_DELETE:
  3618.  * 1.  Try to put all the current information back into the dictionary.
  3619.  * 2.  If the word given is in the dictionary
  3620.  *   a.  Delete the word.
  3621.  *   b.  Relocate any words which may have been in the linked list under
  3622.  *       the deleted word.
  3623.  */
  3624.  
  3625. ProcessEditWordCommand(hDlg, wParam, lParam)
  3626. HWND hDlg;
  3627. WORD wParam;
  3628. LONG lParam;
  3629.  
  3630.    {
  3631.     int  NewWordLen;
  3632.     PDW pWord;  /* pointer to record corresponding to current word */
  3633.     PDW pSuper; /* pointer to record superior to current word */
  3634.     BOOL TransOk;       /* flag to see if integer translation succeeded */
  3635.     HANDLE hNextWord;   /* next word handle temp storage */
  3636.     static DW TempWord; /* temp copy of word begin considered */
  3637.  
  3638.     switch (wParam)
  3639.        {
  3640.         case ID_OK:
  3641.             if (!PostWord(hDlg)) return;
  3642.  
  3643.             if (SendDlgItemMessage(hDlg, ID_WORD, EM_GETMODIFY, 0, 0L))
  3644.             /* word has been changed, take action */
  3645.                {
  3646.                 /* Remember the present word, in case user declines */
  3647.                 /* adding a new word */
  3648.                 hNextWord = hWord;
  3649.                 /* get the changed word (which may be null) */
  3650.                 NewWordLen = GetDlgItemText(hDlg, ID_WORD, (LPSTR) TempWord.w, 10);
  3651.                 TrimWord(TempWord.w, NewWordLen);
  3652.                 if (strlen(TempWord.w))
  3653.                 /* valid word - is it already in dictionary? */
  3654.                    {
  3655.                     RemoveQu(TempWord.w);
  3656.                     /* Fetch the word from the tree if we can */
  3657.                     if (hWord = FindExactLeaf(TempWord.w, hTreeTop))
  3658.                        {
  3659.                         pWord = (PDW) LocalLock(hWord);
  3660.                         TempWord.Suffix1  = pWord -> Suffix1;
  3661.                         TempWord.Freq = pWord -> Freq;
  3662.                         LocalUnlock(hWord);
  3663.                        }
  3664.                     else
  3665.                     /* add a word that is not already in the dictionary */
  3666.                        {
  3667.                         TempWord.Suffix1 = 0;
  3668.                         TempWord.Freq = 0;
  3669.                         if (MessageBox(hDlg, "Add the word?", "Not in Dictionary",
  3670.                                 MB_YESNO) == IDYES)
  3671.                            {
  3672.                             AddLeaf(TempWord.w, &hTreeTop, 0, 0x8000, NULL);
  3673.                             /* retrieve handle for word we just added */
  3674.                             hWord = FindExactLeaf(TempWord.w, hTreeTop);
  3675.                            }
  3676.                         else
  3677.                             /* if adding new word declined, go back to old */
  3678.                             hWord = hNextWord;
  3679.                        }
  3680.                    }
  3681.                 else
  3682.                 /* if No word entered, go back to previous word */
  3683.                     hWord = hNextWord;
  3684.                 UpdateControls(hDlg);
  3685.                }
  3686. /*          else word has not changed.  Do nothing. */
  3687.             break;
  3688.         case ID_CANCEL:
  3689.             if (!PostWord(hDlg)) return;
  3690.             EndDialog(hDlg, NULL);            /* Exits the dialog box */
  3691.             break;
  3692.         case ID_DELETE:
  3693.             if (!PostWord(hDlg)) return;
  3694.             NewWordLen = GetDlgItemText(hDlg, ID_WORD, (LPSTR) TempWord.w, 10);
  3695.             TrimWord(TempWord.w, NewWordLen);
  3696.             RemoveQu(TempWord.w);
  3697.             if (strlen(TempWord.w))
  3698.             /* valid word - look up in dictionary */
  3699.                {
  3700.                 /* if can find leaf, get rid of it */
  3701.                 if (hWord = FindExactLeaf(TempWord.w, hTreeTop))
  3702.                    {
  3703.                     /* first determine what next word diplayed will be */
  3704.                     /* must do this BEFORE modifying the tree */
  3705.                     hNextWord = FindPrev(hWord);  /* could be null */
  3706.                     if (!hNextWord) hNextWord = FindNext(hWord);  /* could be null */
  3707.  
  3708.                     pWord = (PDW) LocalLock(hWord);
  3709.                     /* delete reference in parent leaf */
  3710.                     if (pWord -> up)
  3711.                        {
  3712.                         pSuper = (PDW) LocalLock(pWord -> up);
  3713.                         /* null out reference to the word */
  3714.                         if (pSuper->lt == hWord) pSuper->lt = NULL;
  3715.                         if (pSuper->gt == hWord) pSuper->gt = NULL;
  3716.                         LocalUnlock(pWord -> up);
  3717.                        }
  3718.                     else
  3719.                     /* rare case that this word has no superior, it IS TreeTop */
  3720.                        {
  3721.                         hTreeTop = NULL;
  3722.                        }
  3723.                     /* relocate inferior leaves, if any, from deleted word */
  3724.                     if (pWord -> lt) RelocateLeaf(pWord -> lt, &hTreeTop, NULL);
  3725.                     if (pWord -> gt) RelocateLeaf(pWord -> gt, &hTreeTop, NULL);
  3726.                     LocalUnlock(hWord);
  3727.                     LocalFree(hWord);
  3728.                     hWord = hNextWord;
  3729.                     DictChanged = TRUE;
  3730.                    }
  3731.                 UpdateControls(hDlg);
  3732.                }
  3733.             break;
  3734.         case ID_PREV:
  3735.             if (!PostWord(hDlg)) return;
  3736.             hNextWord = FindPrev(hWord);
  3737.             /* Avoid running off end of list */
  3738.             if (hNextWord) hWord = hNextWord;
  3739.             UpdateControls(hDlg);
  3740.             break;
  3741.         case ID_NEXT:
  3742.             if (!PostWord(hDlg)) return;
  3743.             hNextWord = FindNext(hWord);
  3744.             /* Avoid running off end of list */
  3745.             if (hNextWord) hWord = hNextWord;
  3746.             UpdateControls(hDlg);
  3747.             break;
  3748.         case ID_VIRGIN:
  3749.             if (!PostWord(hDlg)) return;
  3750.             hNextWord = FindVirgin(hTreeTop);
  3751.             if (hNextWord)
  3752.                 hWord = hNextWord;
  3753.             else
  3754.                 MessageBox(hDlg, "No virgin words in dictionary.",
  3755.                     "", MB_OK | MB_ICONASTERISK);
  3756.             UpdateControls(hDlg);
  3757.             break;
  3758.         default:
  3759.             break;
  3760.        }
  3761.    }
  3762.  
  3763. /* Enable and update the fields in the dialog box to match the word
  3764.  * corresponding to handle hWord.  If hWord is null, disable the
  3765.  * appropriate fields.
  3766.  */
  3767. UpdateControls(hDlg)
  3768. HWND hDlg;
  3769.    {
  3770.     PDW LeafPtr;
  3771.     int nControl;       /* used for hiding and unhiding windows */
  3772.     HWND hControl;      /* used for hiding and unhiding windows */
  3773.     
  3774.     if (hWord)
  3775.        {
  3776.         LeafPtr = (PDW) LocalLock(hWord);
  3777.  
  3778.         /* display the word and its frequency */
  3779.         AddQu(LeafPtr -> w);
  3780.         SetDlgItemText(hDlg, ID_WORD, (LPSTR) LeafPtr -> w);
  3781.         SetDlgItemInt(hDlg, ID_FREQ, LeafPtr -> Freq, FALSE);
  3782.         /* display the suffixes with proper check marks */
  3783.         UpdateButtons(hDlg, LeafPtr -> w, LeafPtr -> Suffix1);
  3784.         /* show controls to allow editing */
  3785.         for (nControl=ID_FIRSTEDIT; nControl <= ID_LASTEDIT; nControl++)
  3786.                {
  3787.                 hControl = GetDlgItem(hDlg, nControl);
  3788.                 SetWindowLong(hControl, GWL_STYLE,
  3789.                         ~WS_DISABLED & GetWindowLong(hControl, GWL_STYLE));
  3790.                 InvalidateRect(hControl, NULL, FALSE);
  3791.                }
  3792.         RemoveQu(LeafPtr -> w);
  3793.         LocalUnlock(hWord);
  3794.        }
  3795.     else
  3796.        {
  3797.         /* blank out the word */
  3798.         SetDlgItemText(hDlg, ID_WORD, "");
  3799.         SetDlgItemText(hDlg, ID_FREQ, "");
  3800.         /* blank out the suffix check boxes */
  3801.         for (nControl=ID_FIRSTSUF; nControl <= ID_LASTSUF; nControl++)
  3802.            {
  3803.             SetDlgItemText(hDlg, nControl, "");
  3804.             CheckDlgButton(hDlg, nControl, 0);
  3805.            }
  3806.         /* disable controls which are only appropriate when word is selected */
  3807.         for (nControl=ID_FIRSTEDIT; nControl <= ID_LASTEDIT; nControl++)
  3808.            {
  3809.             hControl = GetDlgItem(hDlg, nControl);
  3810.             SetWindowLong(hControl, GWL_STYLE,
  3811.                 WS_DISABLED | GetWindowLong(hControl, GWL_STYLE));
  3812.             InvalidateRect(hControl, NULL, FALSE);
  3813.             }
  3814.         /* Give the focus to the word to enter */
  3815.         SetFocus(GetDlgItem(hDlg, ID_WORD));
  3816.        }
  3817.    }
  3818.  
  3819. /* update all the buttons with their proper checks */
  3820. UpdateButtons(hDlg, RootWord, Suffixes)
  3821. HWND hDlg;
  3822. char RootWord[];
  3823. unsigned Suffixes;
  3824.    {
  3825.     int nControl;
  3826.     char FullWord[13];
  3827.     unsigned BitMask;
  3828.     
  3829.     BitMask = 1;
  3830.     
  3831.     for (nControl=ID_FIRSTSUF; nControl <= ID_LASTSUF; nControl++)
  3832.        {
  3833.         AddSuffix(RootWord, nControl, FullWord);
  3834.         SetDlgItemText(hDlg, nControl, FullWord);
  3835.         if (BitMask & Suffixes)
  3836.         /* if the bit is set, check the box */
  3837.             CheckDlgButton(hDlg, nControl, 1);
  3838.         else
  3839.             CheckDlgButton(hDlg, nControl, 0);
  3840.         BitMask <<= 1;  /* move to the next bit */
  3841.        }
  3842.    }
  3843.  
  3844. /* Add the suffix specified by suf_id to the RootWord.  Store result in dest */
  3845. /* Warning: this routine may barf if given words shorter than 3 letters */
  3846. AddSuffix(RootWord, suf_id, Dest)
  3847. char RootWord[], Dest[];
  3848. int suf_id;
  3849.    {
  3850.     int len;
  3851.     strcpy(Dest, RootWord);
  3852.     len = strlen(Dest);
  3853.     switch (suf_id)
  3854.        {
  3855.         case ID_SUFS:
  3856.             if (Dest[len-1] == 'Y' && Dest[len-2] != 'A')
  3857.                {
  3858.                 Dest[len-1] = NULL;
  3859.                 strcat(Dest, "IES");
  3860.                }
  3861.             else if (Dest[len-1] == 'S' || Dest[len-1] == 'H' || Dest[len-1] == 'X')
  3862.                 strcat(Dest, "ES");
  3863.             else if (Dest[len-1] == 'F')
  3864.                {
  3865.                 Dest[len-1] = NULL;
  3866.                 strcat(Dest, "VES");
  3867.                }
  3868.             else
  3869.                strcat(Dest, "S");
  3870.             break;
  3871.         case ID_SUFED:
  3872.             if (Dest[len-1] == 'E')
  3873.                 strcat(Dest, "D");
  3874.             else if (Dest[len-1] == 'Y' && Dest[len-2] != 'A')
  3875.                {
  3876.                 Dest[len-1] = 'I';
  3877.                 strcat(Dest, "ED");
  3878.                }
  3879.             else if (isdoub(Dest[len-1]) && isvowel(Dest[len-2]) && !isvowel(Dest[len-3]))
  3880.             /* if word ends in consonant-vowel-consonant, double last letter */
  3881.                {
  3882.                 Dest[len] = Dest[len-1];
  3883.                 Dest[len+1] = NULL;
  3884.                 strcat(Dest, "ED");
  3885.                }
  3886.             else
  3887.                 strcat(Dest, "ED");
  3888.  
  3889.             break;
  3890.         case ID_SUFER:
  3891.             if (Dest[len-1] == 'E')
  3892.                 strcat(Dest, "R");
  3893.             else if (Dest[len-1] == 'Y' && Dest[len-2] != 'A')
  3894.                {
  3895.                 Dest[len-1] = 'I';
  3896.                 strcat(Dest, "ER");
  3897.                }
  3898.             else if (isdoub(Dest[len-1]) && isvowel(Dest[len-2]) && !isvowel(Dest[len-3]))
  3899.             /* if word ends in consonant-vowel-consonant, double last letter */
  3900.                {
  3901.                 Dest[len] = Dest[len-1];
  3902.                 Dest[len+1] = NULL;
  3903.                 strcat(Dest, "ER");
  3904.                }
  3905.             else
  3906.                 strcat(Dest, "ER");
  3907.             break;
  3908.         case ID_SUFEN:
  3909.             if (Dest[len-1] == 'E')
  3910.                 strcat(Dest, "N");
  3911.             else if (isdoub(Dest[len-1]) && isvowel(Dest[len-2]) && !isvowel(Dest[len-3]))
  3912.             /* if word ends in consonant-vowel-consonant, double last letter */
  3913.                {
  3914.                 Dest[len] = Dest[len-1];
  3915.                 Dest[len+1] = NULL;
  3916.                 strcat(Dest, "EN");
  3917.                }
  3918.             else
  3919.                 strcat(Dest, "EN");
  3920.             break;
  3921.         case ID_SUFY:
  3922.             if (Dest[len-1] == 'E')
  3923.                 Dest[len-1] = 'Y';
  3924.             else if (isdoub(Dest[len-1]) && isvowel(Dest[len-2]) && !isvowel(Dest[len-3]))
  3925.             /* if word ends in consonant-vowel-consonant, double last letter */
  3926.                {
  3927.                 Dest[len] = Dest[len-1];
  3928.                 Dest[len+1] = 'Y';
  3929.                 Dest[len+2] = NULL;
  3930.                }
  3931.             else
  3932.                 strcat(Dest, "Y");
  3933.             break;
  3934.         case ID_SUFLY:
  3935.             strcat(Dest, "LY");
  3936.             break;
  3937.         case ID_SUFEST:
  3938.             if (Dest[len-1] == 'E')
  3939.                 strcat(Dest, "ST");
  3940.             else if (Dest[len-1] == 'Y' && Dest[len-2] != 'A')
  3941.                {
  3942.                 Dest[len-1] = 'I';
  3943.                 strcat(Dest, "EST");
  3944.                }
  3945.             else if (isdoub(Dest[len-1]) && isvowel(Dest[len-2]) && !isvowel(Dest[len-3]))
  3946.             /* if word ends in consonant-vowel-consonant, double last letter */
  3947.                {
  3948.                 Dest[len] = Dest[len-1];
  3949.                 Dest[len+1] = NULL;
  3950.                 strcat(Dest, "EST");
  3951.                }
  3952.             else
  3953.                 strcat(Dest, "EST");
  3954.             break;
  3955.         case ID_SUFISH:
  3956.             if (Dest[len-1] == 'E') Dest[len-1] = NULL;
  3957.             strcat(Dest, "ISH");
  3958.             break;
  3959.         case ID_SUFING:
  3960.         /* drop the e before ing */
  3961.             if (Dest[len-1] == 'E')
  3962.                {
  3963.                 Dest[len-1] = NULL;
  3964.                 strcat(Dest, "ING");
  3965.                }
  3966.             else if (isdoub(Dest[len-1]) && isvowel(Dest[len-2]) && !isvowel(Dest[len-3]))
  3967.             /* if word ends in consonant-vowel-consonant, double last letter */
  3968.                {
  3969.                 Dest[len] = Dest[len-1];
  3970.                 Dest[len+1] = NULL;
  3971.                 strcat(Dest, "ING");
  3972.                }
  3973.             else
  3974.                strcat(Dest, "ING");
  3975.             break;
  3976.         case ID_SUFERS:
  3977.             if (Dest[len-1] == 'E')
  3978.                 strcat(Dest, "RS");
  3979.             else if (Dest[len-1] == 'Y' && Dest[len-2] != 'A')
  3980.                {
  3981.                 Dest[len-1] = 'I';
  3982.                 strcat(Dest, "ERS");
  3983.                }
  3984.             else if (isdoub(Dest[len-1]) && isvowel(Dest[len-2]) && !isvowel(Dest[len-3]))
  3985.             /* if word ends in consonant-vowel-consonant, double last letter */
  3986.                {
  3987.                 Dest[len] = Dest[len-1];
  3988.                 Dest[len+1] = NULL;
  3989.                 strcat(Dest, "ERS");
  3990.                }
  3991.             else
  3992.                 strcat(Dest, "ERS");
  3993.             break;
  3994.         case ID_SUFINGS:
  3995.         /* drop the e before ing */
  3996.             if (Dest[len-1] == 'E')
  3997.                {
  3998.                 Dest[len-1] = NULL;
  3999.                 strcat(Dest, "INGS");
  4000.                }
  4001.             else if (isdoub(Dest[len-1]) && isvowel(Dest[len-2]) && !isvowel(Dest[len-3]))
  4002.             /* if word ends in consonant-vowel-consonant, double last letter */
  4003.                {
  4004.                 Dest[len] = Dest[len-1];
  4005.                 Dest[len+1] = NULL;
  4006.                 strcat(Dest, "INGS");
  4007.                }
  4008.             else
  4009.                strcat(Dest, "INGS");
  4010.             break;
  4011.        }
  4012.    }
  4013.  
  4014. /* number of bad combinations to search */
  4015. #define NBADCHARS 274
  4016.  
  4017. static char badchars[NBADCHARS][3] = {
  4018. "AA","BC","BD","BF","BG","BH",
  4019. "BK","BN","BP","BQ","BV","BW","BX","BZ",
  4020. "CB","CD","CF","CG","CJ","CP","CQ",
  4021. "CV","CW","CX","CZ","DC","DF","DH","DK",
  4022. "DP","DQ","DX","DZ","FB","FC","FD",
  4023. "FG","FH","FJ","FK","FM","FN","FP","FQ","FV",
  4024. "FW","FX","FZ","GB","GC","GD","GF","GJ","GK","GP",
  4025. "GQ","GT","GV","GW","GX","GZ","HB","HC","HD",
  4026. "HF","HG","HH","HJ","HK","HL","HP","HQ",
  4027. "HS","HV","HW","HX","HZ","IH","II","IW","IY",
  4028. "JB","JC","JD","JF","JG","JH","JJ","JK","JL","JM",
  4029. "JN","JP","JQ","JR","JS","JT","JV","JW","JX","JY",
  4030. "JZ","KB","KC","KD","KF","KG","KH","KJ","KK","KM",
  4031. "KP","KQ","KR","KT","KV","KW","KX","KZ","LH",
  4032. "LJ","LQ","LR","LX","LZ","MC","MD",
  4033. "MG","MH","MJ","MK","ML","MQ","MR","MV",
  4034. "MW","MX","MZ","NQ",
  4035. "OJ","OQ","PC","PF","PG","PJ",
  4036. "PK","PM","PN","PQ","PV","PX","PZ","QA","QB",
  4037. "QC","QD","QE","QF","QG","QH","QI","QJ","QK","QL",
  4038. "QM","QN","QO","QP","QQ","QR","QS","QT","QV","QW",
  4039. "QX","QY","QZ","RJ","RX","RZ","SB",
  4040. "SF","SG","SJ","SR","SV","SX","SZ","TB","TD","TG",
  4041. "TJ","TK","TP","TQ","TV","TX","UH",
  4042. "UJ","UQ","UV","UW","VB","VC","VD",
  4043. "VF","VG","VH","VJ","VK","VL","VM","VN","VP","VQ",
  4044. "VR","VS","VT","VU","VV","VW","VX","VZ","WC",
  4045. "WG","WJ","WP","WQ","WV",
  4046. "WW","WX","WZ","XB","XD","XF","XG","XJ",
  4047. "XK","XM","XN","XQ","XR","XS","XV",
  4048. "XW","XX","XZ","YF","YH","YJ","YK",
  4049. "YQ","YU","YV","YY","YZ","ZB","ZC","ZD",
  4050. "ZF","ZG","ZH","ZJ","ZK","ZM","ZN","ZP","ZQ","ZR",
  4051. "ZS","ZT","ZV","ZW","ZX"
  4052. };
  4053.  
  4054. /* A cheap, inexact algorithm to cull out strings of letters */
  4055. /* that are probably not English words */
  4056. /* Based on finding unlikely two-character sequences in the string */
  4057. /* Warning: expects input to be capital-letters ONLY.  Lowercase letters */
  4058. /* make the routine asssume the word is valid */
  4059.  
  4060. BOOL isword(TheWord)
  4061.     char TheWord[];
  4062.    {
  4063.     int pos;
  4064.     int EndCount;
  4065.     char tstr[3];
  4066.     int VowelCount;
  4067.     
  4068.     /* check vowel count - don't want all vowels, or no vowels */
  4069.     EndCount = strlen(TheWord);
  4070.     VowelCount = 0;
  4071.     for (pos=0; pos<EndCount; pos++)
  4072.         if (isvowel(TheWord[pos])) VowelCount++;
  4073.     if (VowelCount==0 || VowelCount==EndCount) return(FALSE);
  4074.  
  4075.     EndCount -= 2;
  4076.     if (EndCount < 0) return(FALSE);
  4077.  
  4078.     for (pos=0; pos <= EndCount; pos++)
  4079.        {
  4080.         tstr[0] = TheWord[pos];
  4081.         tstr[1] = TheWord[pos+1];
  4082.         tstr[2] = 0;
  4083.         if (bsearch(tstr, badchars, NBADCHARS, 3, strcmp)) return(FALSE);
  4084.        }
  4085.     return(TRUE);
  4086.    }
  4087.  
  4088. /* These variables are applicable to the timer window only.  */
  4089. /* If this were an object-oriented language, they would be in */
  4090. /* the domain of the set of timer subroutines */
  4091.  
  4092. WORD idGameTimer;      /* ID of the timer which ticks every second */
  4093. int GameTime;           /* used by Egg Timer window */
  4094. /* Pens stay around until window closed */
  4095. HPEN SandPen, FallingPen, EraserPen, GlassPen;
  4096. HBRUSH SandBrush, EraserBrush;
  4097. double K1;  /* constant used to compute drawing of sand */
  4098.  
  4099. /****************************************************************************
  4100.  
  4101.     FUNCTION: EggWndProc(HWND, unsigned, WORD, LONG)
  4102.  
  4103.     PURPOSE:  Processes messages
  4104.  
  4105.     MESSAGES:
  4106.  
  4107.         WM_CREATE     - create window
  4108.         WM_PAINT      - repaint window
  4109.         WM_TIMER        - timer tick
  4110.         EGGM_SET        - sets timeout value
  4111.         EGGM_START      - start timer
  4112.         EGGM_STOP       - stop timer (force to end state)
  4113.         WM_TIMER        - 1 second game timer tick
  4114.         WM_DESTROY    - destroy window
  4115.  
  4116.     COMMENTS:
  4117.  
  4118.         This is the windows procedure for the Egg Timer window.
  4119.         Once started, the egg timer automatically updates itself
  4120.         every second until the timeout value is reached.  A message
  4121.         is then sent to the parent window to indicate that the timer
  4122.         has run out.
  4123.  
  4124. ****************************************************************************/
  4125.  
  4126. long FAR PASCAL EggWndProc(hEgg, message, wParam, lParam)
  4127. HWND hEgg;                                /* window handle                   */
  4128. unsigned message;                         /* type of message                 */
  4129. WORD wParam;                              /* additional information          */
  4130. LONG lParam;                              /* additional information          */
  4131. {
  4132.     PAINTSTRUCT ps;                       /* used by paint procedure */
  4133.     HDC hDC;                              /* used by paint procedure */
  4134.  
  4135.     switch (message) {
  4136.         case WM_CREATE:                     /* message: window being created */
  4137.             ProcessEggCreate(hEgg);
  4138.             break;
  4139.  
  4140.         case WM_PAINT:
  4141.             hDC = BeginPaint(hEgg, (LPPAINTSTRUCT) &ps);
  4142.             ProcessEggPaint(hEgg, hDC);
  4143.             EndPaint(hEgg, (LPPAINTSTRUCT) &ps);
  4144.             break;
  4145.  
  4146.         case EGGM_SET:
  4147.             /* wParam is timeout value in seconds.  Refuse nonpositive values */
  4148.             EndTime = max(wParam, 1);
  4149.             K1 = 60. * pow(10. / EndTime, 0.3333);
  4150.             break;
  4151.  
  4152.         case WM_LBUTTONDOWN:
  4153.             PostMessage(GetParent(hEgg), WM_COMMAND, MN_DD_GAME + MN_GAM_PLAY, 0L);
  4154.             break;
  4155.  
  4156.         case EGGM_START:
  4157.             ProcessEggStart(hEgg);
  4158.             break;
  4159.  
  4160.         case EGGM_STOP:
  4161.             if (!GameOver)
  4162.                {
  4163.                 if (idGameTimer) KillTimer(hEgg, ID_GT); idGameTimer = 0;
  4164.                 /* Reset the picture, too */
  4165.                 GameTime = EndTime;
  4166.                 /* Want timer to be redrawn immediately, especially before scoring */
  4167.                 InvalidateRect(hEgg, NULL, TRUE);
  4168.                 UpdateWindow(hEgg);
  4169.                 /* Send message to parent, saying timer popped */
  4170.                 PostMessage(GetParent(hEgg), BAGOM_ENDGAME, 0, 0L);
  4171.                }
  4172.             break;
  4173.             
  4174.         case WM_TIMER:
  4175.             ProcessEggTimer(hEgg);
  4176.             break;
  4177.             
  4178.         case WM_DESTROY:                  /* message: window being destroyed */
  4179.             if (idGameTimer) KillTimer(hEgg, ID_GT); idGameTimer = 0;
  4180.             /* put away our pens and brushes */
  4181.             DeleteObject(SandPen);
  4182.             DeleteObject(FallingPen);
  4183.             DeleteObject(SandBrush);
  4184.             PostMessage(GetParent(hEgg), WM_SETFOCUS, hEgg, 0L);
  4185.             break;
  4186.  
  4187.         default:                          /* Passes it on if unproccessed    */
  4188.             return (DefWindowProc(hEgg, message, wParam, lParam));
  4189.     }
  4190.     return (NULL);
  4191. }
  4192.  
  4193. /* Egg Timer processing routines */
  4194. ProcessEggCreate(hEgg)
  4195. HWND hEgg;
  4196.    {
  4197.     GlassPen = GetStockObject(BLACK_PEN);
  4198.     SandPen = CreatePen(0, 1, RED);
  4199.     SandBrush = CreateSolidBrush(RED);
  4200.     FallingPen = CreatePen(2, 1, RED);
  4201.     EraserPen = CreatePen(0, 1, Mono ? WHITE : GRAY1);
  4202.     EraserBrush = hGrayBrush1;
  4203.     GameTime = EndTime;
  4204.     /* This is also recomputed every time the game length is set */
  4205.     K1 = 60. * pow(10. / EndTime, 0.3333);
  4206.     SetFocus(hEgg);
  4207.    }
  4208.  
  4209. /* This routine is called every time the 1-second timer goes off */
  4210. ProcessEggTimer(hEgg)
  4211. HWND hEgg;
  4212.    {
  4213.     if (GameTime >= EndTime)
  4214.     /* This round is over */
  4215.        {
  4216.         if (idGameTimer) KillTimer(hEgg, ID_GT); idGameTimer = 0;  /* stop the timer */
  4217.         /* Send message to parent, saying timer popped */
  4218.         PostMessage(GetParent(hEgg), BAGOM_ENDGAME, 0, 0L);
  4219.        }
  4220.     else
  4221.         GameTime++;
  4222.  
  4223.     InvalidateRect(hEgg, NULL, FALSE);
  4224.     UpdateWindow(hEgg);
  4225.    }
  4226.  
  4227. /* Set up device context for Egg Timer window. */
  4228. /* Regardless of physical dimensions of window, want to map a grid */
  4229. /* of +-70 wide and +-290 high */
  4230. SetupEggDC (hEgg, hDC)
  4231. HWND    hEgg;
  4232. HDC     hDC;
  4233.    {
  4234.     RECT rClient;
  4235.  
  4236.     GetClientRect(hEgg, &rClient);
  4237.  
  4238.     SetMapMode (hDC, MM_ANISOTROPIC);
  4239.     SetWindowOrg (hDC, -70, -290);
  4240.     SetWindowExt( hDC, 140, 580);
  4241.     SetViewportOrg (hDC, rClient.left, rClient.bottom);
  4242.     SetViewportExt (hDC, rClient.right-rClient.left, rClient.top-rClient.bottom);
  4243.     SetPolyFillMode(hDC, ALTERNATE);
  4244.     SetBkMode(hDC, TRANSPARENT);
  4245.     return(TRUE);
  4246.    }
  4247.  
  4248. /* This draws the picture of the Egg timer and sand.
  4249.  * The routine is written with low flicker as the first priority.
  4250.  * Speed is important, too, but it is second priority.
  4251.  * In trying to simulate the real way sand looks in an hourglass,
  4252.  * the height of the sand varies linearly with time as long as the
  4253.  * top of the sand is in the cylindrical part of the glass.  When the
  4254.  * top of the sand is in the funnel part of the glass, the height varies
  4255.  * as the cube root of time.  In other words, the sand level drops faster
  4256.  * and faster once it reaches the funnel.
  4257.  */
  4258.     /* The outline of the Egg Timer glass */
  4259.     static int Glass[11][2] = {
  4260.         {-10,    0},
  4261.         {-50,   60},
  4262.         {-50,  270},
  4263.         { 50,  270},
  4264.         { 50,   60},
  4265.         { 10,    0},
  4266.         { 50,  -60},
  4267.         { 50, -270},
  4268.         {-50, -270},
  4269.         {-50,  -60},
  4270.         {-10,    0} };
  4271.  
  4272.     /* The sand on the top.  Some y-values get overwritten in execution */
  4273.     static int TopSand[5][2] = {
  4274.         {-40,  240},
  4275.         { 40,  240},
  4276.         { 40,   60},
  4277.         {  0,    0},
  4278.         {-40,   60} } ;
  4279.  
  4280.     /* The air in the top section */
  4281.     static int TopAir[6][2] = {
  4282.         {-40,  240},
  4283.         { 40,  240},
  4284.         { 40,   60},
  4285.         {  0,    0},
  4286.         {  0,    0},
  4287.         {-40,   60} } ;
  4288.  
  4289.     /* The sand in the funnel section only */
  4290.     static int FunnelSand[3][2] = {
  4291.         {-40,   60},
  4292.         { 40,   60},
  4293.         {  0,    0} } ;
  4294.  
  4295. ProcessEggPaint(hEgg, hDC)
  4296. HWND hEgg;
  4297. HDC hDC;
  4298.    {
  4299.     HPEN hOldPen;
  4300.     HBRUSH hBrush, hOldBrush;
  4301.     int yTemp;  /* temporary storage to avoid floating point recomputation */
  4302.  
  4303.     SetupEggDC(hEgg, hDC);
  4304.  
  4305.     hOldPen = SelectObject(hDC, GlassPen);
  4306.     hOldBrush = SelectObject(hDC, EraserBrush);
  4307.     Polyline(hDC, (LPPOINT) Glass, 11);
  4308.  
  4309.     /* On top, Draw Sand first, then Air (avoid flicker )*/
  4310.     SelectObject(hDC, EraserPen);
  4311.     SelectObject(hDC, SandBrush);
  4312.  
  4313.     if (10*GameTime < 9*EndTime)
  4314.     /* Both in the tube and funnel section on top */
  4315.        {
  4316.         yTemp = 240 - 200L * GameTime / EndTime;
  4317.         /* Only the top of the sand changes */
  4318.         TopSand[0][1] = TopSand[1][1] = yTemp;
  4319.         Polygon(hDC, (LPPOINT) TopSand, 5);
  4320.         SelectObject(hDC, EraserBrush);
  4321.         Rectangle(hDC, -40, 240,
  4322.                         40, yTemp);
  4323.        }
  4324.     else if (GameTime < EndTime)
  4325.     /* Exclusively in the funnel section */
  4326.        {
  4327.         yTemp = K1 * pow(EndTime - GameTime, 0.3333);
  4328.         FunnelSand[0][1] = FunnelSand[1][1] = yTemp;
  4329.         FunnelSand[0][0] =  0.6667 * yTemp;
  4330.         FunnelSand[1][0] = -0.6667 * yTemp;
  4331.         Polygon(hDC, (LPPOINT) FunnelSand, 3);
  4332.         SelectObject(hDC, EraserBrush);
  4333.         TopAir[3][1] = TopAir[4][1] = yTemp;
  4334.         TopAir[3][0] =  0.6667 * yTemp;
  4335.         TopAir[4][0] = -0.6667 * yTemp;
  4336.         Polygon(hDC, (LPPOINT) TopAir, 6);
  4337.        }
  4338.     else
  4339.     /* GameTime >= EndTime (Game over) */
  4340.        {
  4341.         SelectObject(hDC, EraserBrush);
  4342.         TopSand[0][1] = TopSand[1][1] = 240;
  4343.         Polygon(hDC, (LPPOINT) TopSand, 5);
  4344.        }
  4345.  
  4346.     /* show the falling sand except at start and end */
  4347.     if (0 < GameTime && GameTime < EndTime)
  4348.         SelectObject(hDC, FallingPen);
  4349.     else
  4350.         SelectObject(hDC, EraserPen);
  4351.     yTemp =  -260 + 200L * GameTime / EndTime;
  4352.     MoveTo(hDC, 0, 0);
  4353.     LineTo(hDC, 0, yTemp);
  4354.  
  4355.     /* Now draw the sand on the bottom. */
  4356.     SelectObject(hDC, SandPen);
  4357.     SelectObject(hDC, SandBrush);
  4358.     Rectangle(hDC, -40, yTemp,
  4359.                     40, -260);
  4360.     SelectObject(hDC, hOldBrush);
  4361.     SelectObject(hDC, hOldPen);
  4362.    }
  4363.  
  4364. /* Start the Egg Timer */
  4365. ProcessEggStart(hEgg)
  4366. HWND hEgg;
  4367.    {
  4368.     GameTime = 0;
  4369.     idGameTimer = SetTimer(hEgg, ID_GT, 1000, NULL);
  4370.     if (idGameTimer)
  4371.         InvalidateRect(hEgg, NULL, TRUE);
  4372.     else
  4373.     /* timer not created */
  4374.        {
  4375.         MessageBox(hEgg, "Can't get windows timer.", "ERROR",
  4376.             MB_ICONQUESTION | MB_OK);
  4377.         DestroyWindow(hEgg);
  4378.        }
  4379.    }
  4380.  
  4381. /********* Computer Play Routines *********/
  4382. /* global used to build trial word */
  4383. char TrialWord[25] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
  4384. int TrialLen;
  4385.  
  4386. /* Parital string search.  Search the dictionary and return TRUE if the
  4387.  * string TrialWord could possibly be part of a known word.  Also saves
  4388.  * TrialWord in a list if it is exactly a word.  Written for search
  4389.  * efficiency, terminating branches which would clearly be fruitless.
  4390.  * psearch tries to return FALSE whenever possible, as a TRUE return
  4391.  * will cause as many as 8 more psearch calls, not including recursion.
  4392.  * If most of a dictionary word matches TrialWord, this routine tries
  4393.  * appending suffixes to the dictionary word on the fly.
  4394.  * Global TrialLen is actual length of TrialWord.  Should be set
  4395.  * before calling psearch.  Not always equal to strlen(TrialWord),
  4396.  * because Qu counts as 2 letters.  Careful: in some cases, you
  4397.  * actually want strlen(TrialWord)
  4398.  */
  4399. BOOL psearch(hLeaf)
  4400. HANDLE hLeaf;
  4401.    {
  4402.     PDW LeafPtr;
  4403.     int result;   /* result of string compare */
  4404.     BOOL retval;  /* return value */
  4405.     int nSuffix;                /* used for suffixing words */
  4406.     char FullWord[13];
  4407.     unsigned BitMask;
  4408.     
  4409.     /* DBCount++; */
  4410.     if (!hLeaf) return(FALSE);
  4411.     /* assumption that some word will begin with any given single letter */
  4412.     if (strlen(TrialWord) < 2) return(TRUE);
  4413.     LeafPtr = (PDW) LocalLock(hLeaf);
  4414.  
  4415.     result = strncmp(TrialWord, LeafPtr -> w, 2);
  4416.     /* check 1st 2 characters to start. */
  4417.     /* if trial=RATS, and current leaf is RATE, we would never go */
  4418.     /* to RAT to try suffixes otherwise.  This greatly degrades the */
  4419.     /* searching efficiency, but provides enables the above case to */
  4420.     /* be found */
  4421.     if (result < 0)
  4422.     /* trial word definitely less than dictionary word. */
  4423.        {
  4424.         retval = psearch(LeafPtr -> lt);
  4425.        }
  4426.     else if (result > 0)
  4427.        {
  4428.         retval = psearch(LeafPtr -> gt);
  4429.        }
  4430.     else
  4431.     /* result == 0; first 2 characters match between trial and dict */
  4432.        {
  4433.         result = strcmp(TrialWord, LeafPtr -> w);
  4434.         if (!result)
  4435.         /* Perfect match.  Record word and exit */
  4436.         /* We know that we can return TRUE, because we already match the */
  4437.         /* root word, and Trial is probably a subset of some suffixed version */
  4438.            {
  4439.             if (TrialLen > 3)
  4440.             /* only record words of legal size */
  4441.                {
  4442.                 if ((rand() % 100) < Smartness)
  4443.                 /* According to skill level, computer may miss some words */
  4444.                    {
  4445.                     AddLeaf(TrialWord, &hCListTop, 0, 0, NULL);
  4446.                     /* computer find counts as a usage */
  4447.                     if ((LeafPtr -> Freq) < 32767) (LeafPtr -> Freq)++;
  4448.                    }
  4449.                }
  4450.             retval = TRUE;
  4451.            }
  4452.         else
  4453.         /* Non-perfect match.  Must still consider lower leaves on */
  4454.         /* both gt and lt leaves */
  4455.            {
  4456.             /* if trial is a subset of dict word, return TRUE */
  4457.             retval = !strncmp(TrialWord, LeafPtr->w, strlen(TrialWord));
  4458.  
  4459.             /* only search further if there's a chance we can find a */
  4460.             /* legal word, or we are not sure whether this TrialWord */
  4461.             /* will match part of some dictionary word */
  4462.             if (TrialLen > 3 || !retval)
  4463.                 if (psearch(LeafPtr -> lt)) retval = TRUE;
  4464.             if (TrialLen > 3 || !retval)
  4465.                 if (psearch(LeafPtr -> gt)) retval = TRUE;
  4466.             if ((TrialLen > 3 || !retval) &&
  4467.                 !strncmp(TrialWord, LeafPtr->w, strlen(LeafPtr->w)-1))
  4468.             /* As last resort, try suffixes.  Costly operation.  */
  4469.                {
  4470.                 BitMask = 1;
  4471.     
  4472.                 for (nSuffix=ID_FIRSTSUF; nSuffix <= ID_LASTSUF; nSuffix++)
  4473.                    {
  4474.                     if (BitMask & (LeafPtr->Suffix1))
  4475.                     /* if the suffix is legal, try it */
  4476.                        {
  4477.                         AddSuffix(LeafPtr->w, nSuffix, FullWord);
  4478.                         if (!strcmp(TrialWord, FullWord))
  4479.                         /* matches suffixed word exactly */
  4480.                            {
  4481.                             if ((rand() % 100) < Smartness)
  4482.                             /* Force computer to miss some according to level */
  4483.                                {
  4484.                                 AddLeaf(TrialWord, &hCListTop, 0, 0, NULL);
  4485.                                 /* computer find counts as a usage */
  4486.                                 if ((LeafPtr -> Freq) < 32767) (LeafPtr -> Freq)++;
  4487.                                }
  4488.                            }
  4489.                         else
  4490.                         /* possibility of trial="MAKI", return true for "MAKING" */
  4491.                            {
  4492.                             if (!retval && !strncmp(TrialWord, FullWord, strlen(TrialWord)))
  4493.                             retval = TRUE;
  4494.                            }
  4495.                        }
  4496.                     BitMask <<= 1;  /* move to the next bit */
  4497.                     }
  4498.                }
  4499.            }
  4500.        }
  4501.     LocalUnlock(hLeaf);
  4502.     return(retval);
  4503.    }
  4504.  
  4505. /* This routine searches all possible words on paths starting from the
  4506.  * die pointed to by CubePtr
  4507.  * It is assumed that a subroutine will be called from this routine to
  4508.  * determine whether any search path leads to a dead end.  Otherwise,
  4509.  * this routine will search all 25! + 24! + 23! ... + 1! combinations!
  4510.  * The trial path is built in a global, TrialWord, for speed reasons.
  4511.  */
  4512. SearchBoard(CubePtr)
  4513. struct tagBOARD *CubePtr;
  4514.    {
  4515.     int l;  /* length of trial string so far */
  4516.     int neighbor;
  4517.     MSG msg;  /* for PeekMessage yield */
  4518.     char RealWord[11];  /* TrialWord with Qu expanded */
  4519.  
  4520.     if (GameOver) return;       /* abort search if game over */
  4521.  
  4522.     /* We want to yield, so as not to hog CPU resources with the word search */
  4523.     /* Allow the message queue to clear */
  4524.     /* Otherwise, window will appear to hang while the computer is searching */
  4525.     while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  4526.        {
  4527.         TranslateMessage(&msg);
  4528.         DispatchMessage(&msg);
  4529.        }
  4530.  
  4531.     if (CubePtr == NULL || (CubePtr -> Cused)) return;  /* not a viable path */
  4532.     CubePtr -> Cused = TRUE;
  4533.     l = strlen(TrialWord);
  4534.     TrialWord[l] = CubePtr -> val;  /* append letter to search string */
  4535.     strcpy(RealWord, TrialWord);        /* find length of real word with Qu */
  4536.     AddQu(RealWord);
  4537.     TrialLen = strlen(RealWord);
  4538.     if (psearch(hTreeTop))
  4539.        {
  4540.         for (neighbor=0; neighbor < 8; neighbor++)
  4541.             SearchBoard(CubePtr -> link[neighbor]);     
  4542.        }
  4543.     TrialWord[l] = 0;  /* remove the character we appended */
  4544.     CubePtr -> Cused = FALSE;
  4545.    }
  4546.  
  4547. /* This routine recursively adds a leaf and all leaves below it in a
  4548.  * tree to the computer word list.  Used to display the words that the
  4549.  * computer found when the game is over.
  4550.  */
  4551. DisplayCList(hLeafPtr)
  4552. HANDLE hLeafPtr;
  4553.    {
  4554.     PDW LeafPtr;
  4555.     
  4556.     if (hLeafPtr)
  4557.        {
  4558.         LeafPtr = (PDW) LocalLock(hLeafPtr);
  4559.         DisplayCList(LeafPtr -> lt);    /* print all less than */
  4560.         AddQu(LeafPtr -> w);  /* Change Q to Qu for display purposes */
  4561.         SendMessage(hCList, LB_INSERTSTRING, -1, (LONG) (LPSTR) LeafPtr->w);
  4562.         DisplayCList(LeafPtr -> gt);    /* print all greater than */
  4563.         LocalUnlock(hLeafPtr);
  4564.        }
  4565.    }
  4566.  
  4567. /* Sets up the next rack, which will be copied to the board on the next */
  4568. /* call to NewGame.  First, all the dice in the master list (dice[]) are */
  4569. /* set to the unused state.  Then, a die is randomly selected and placed */
  4570. /* on the board, with a random face up.  This routine insures by design */
  4571. /* that no master die is used more than once */
  4572. SetNextRack(Rack)
  4573. unsigned Rack;
  4574.    {
  4575.     int row, col;  /* used to step through board array */
  4576.     int cubeno;  /* used to index master list of cubes */
  4577.     
  4578.     srand(Rack);
  4579.  
  4580.     for (cubeno=0; cubeno < NDICE; cubeno++)
  4581.         dice[cubeno][0] = FALSE;
  4582.  
  4583.     /* fill every space on the board */
  4584.     for (row=0; row < NROWS; row++)
  4585.         for (col=0; col < NCOLS; col++)
  4586.            {
  4587.             /* keep trying until a free cube is found */
  4588.             do { cubeno = rand() % NDICE; } while (dice[cubeno][0]);
  4589.             /* pick a face from the die, and an orientation */
  4590.             NextVal[row][col] = dice[cubeno][(rand() % NFACES)+1];
  4591.             NextOrient[row][col] = rand() % NORIENT;
  4592.             dice[cubeno][0] = TRUE;       /* mark the cube as used */
  4593.            }
  4594.    }
  4595.  
  4596. /* This function creates the progress box window.  Do NOT call this */
  4597. /* function again before destroying the corresponding window */
  4598. /* Parameter passed in is handle of parent window */
  4599. /* The handle to the progress box window is returned */
  4600. HWND CreateProBox(hWnd)
  4601. HWND hWnd;
  4602.    {
  4603.     HWND hPro;
  4604.  
  4605.     hPro = CreateWindow("Pro",          /* window class */
  4606.         "",                             /* window name       */
  4607.         WS_POPUP | WS_VISIBLE | WS_DLGFRAME,
  4608.         0, 0, 0, 0,                     /* Leave sizing to window proc */
  4609.         hWnd,                           /* parent handle        */
  4610.         NULL,                      /* menu or child ID     */
  4611.         hInst,                          /* instance             */
  4612.         NULL);                          /* additional info      */
  4613.  
  4614.     BringWindowToTop(hPro);
  4615.     return(hPro);
  4616.    }
  4617.  
  4618. /****************************************************************************
  4619.  
  4620.     FUNCTION: ProWndProc(HWND, unsigned, WORD, LONG)
  4621.  
  4622.     PURPOSE:  Processes messages
  4623.  
  4624.     COMMENTS:
  4625.  
  4626.         This is the windows procedure for the Progress Box.
  4627.         Similar to the progress bar.  Used to placate user while
  4628.         a lengthy operation is going on.  WARNING: only accomodates
  4629.         range to the limit of unsigned int (probably 64k).
  4630.  
  4631. ****************************************************************************/
  4632.  
  4633. long FAR PASCAL ProWndProc(hPro, message, wParam, lParam)
  4634. HWND hPro;                                /* window handle                   */
  4635. unsigned message;                         /* type of message                 */
  4636. WORD wParam;                              /* additional information          */
  4637. LONG lParam;                              /* additional information          */
  4638. {
  4639.     static LPSTR ProMsg;  /* the message to be displayed by progress box */
  4640.     static unsigned ProRange;   /* range of bar will be 0 - ProRange */
  4641.     static unsigned ProPos;             /* current position */
  4642.  
  4643.     PAINTSTRUCT ps;                       /* used by paint procedure */
  4644.     HDC hDC;                              /* used by paint procedure */
  4645.  
  4646.     static TEXTMETRIC tmFont;           /* All this for initial size, place */
  4647.     int x, y;                           /* placement of ProBox */
  4648.     static int height, width;           /* calculated size of ProBox */
  4649.     HWND hWnd;                          /* handle of parent */
  4650.     RECT Rect;
  4651.     POINT Point;
  4652.     static HBRUSH hMagBrush;
  4653.     
  4654.     char OutBuf[5];                     /* for the percentage */
  4655.  
  4656.     switch (message) {
  4657.  
  4658.         /* PRO_INIT should come in via SendMessage just after creation */
  4659.         /* The wParam sets the range of the Progress bar. */
  4660.         /* lParam sets the message text */
  4661.         /* This code will make a progress box big enough for the text, */
  4662.         /* and center it in the client area of the caller */
  4663.         case PRO_INIT:
  4664.             ProMsg = (LPSTR) lParam;
  4665.             ProRange = wParam;
  4666.             ProPos = 0;
  4667.             hWnd = GetParent(hPro);
  4668.             GetClientRect(hWnd, (LPRECT) &Rect);
  4669.             hDC = GetDC(hPro);
  4670.             GetTextMetrics(hDC, &tmFont);       /* size the window */
  4671.             height = tmFont.tmHeight * 6;
  4672.             width  = 2*tmFont.tmAveCharWidth + LOWORD(GetTextExtent(hDC, ProMsg, lstrlen(ProMsg)));
  4673.             x = (Rect.right + Rect.left - width) >> 1;  /* center the window */
  4674.             y = (Rect.bottom + Rect.top - height) >> 1;
  4675.             ReleaseDC(hPro, hDC);
  4676.             Point.x = x;        /* convert to screen coordinates for popup */
  4677.             Point.y = y;
  4678.             ClientToScreen(hWnd, &Point);
  4679.             Point.x = max(Point.x, 0);          /* in case parent too small */
  4680.             Point.y = max(Point.y, 0);
  4681.             MoveWindow(hPro, Point.x, Point.y, width, height, TRUE);
  4682.             /* the invalidate is needed in case of init more than once */
  4683.             InvalidateRect(hPro, NULL, TRUE);
  4684.             /* adjust size slightly: client rect <> window size */
  4685.             GetClientRect(hPro, (LPRECT) &Rect);
  4686.             height = Rect.bottom - Rect.top;
  4687.             width  = Rect.right - Rect.left;
  4688.             hMagBrush = CreateSolidBrush(MAGENTA);
  4689.             break;
  4690.  
  4691.         case PRO_SETPOS:
  4692.             ProPos = wParam;
  4693.             InvalidateRect(hPro, NULL, FALSE);
  4694.             break;
  4695.  
  4696.         case WM_PAINT:
  4697.             /* This falls through to the paint code following */
  4698.             break;
  4699.  
  4700.         case WM_DESTROY:
  4701.             DeleteObject(hMagBrush);
  4702.             return(NULL);
  4703.             break;
  4704.  
  4705.         default:                          /* Passes it on if unproccessed    */
  4706.             return (DefWindowProc(hPro, message, wParam, lParam));
  4707.        }
  4708.     /* We do a paint on any message, as we cannot wait for posted WM_PAINT */
  4709.     hDC = BeginPaint(hPro, (LPPAINTSTRUCT) &ps);
  4710.     SetBkColor(hDC, Mono ? WHITE : GRAY1);
  4711.     SetTextAlign(hDC, TA_CENTER | TA_TOP);
  4712.     TextOut(hDC, (ps.rcPaint.right - ps.rcPaint.left) >>1,
  4713.          tmFont.tmHeight, ProMsg, lstrlen(ProMsg));
  4714.     sprintf(OutBuf, "%d%%", 100L*ProPos/ProRange);
  4715.     TextOut(hDC, (ps.rcPaint.right - ps.rcPaint.left) >>1,
  4716.          (tmFont.tmHeight *5) >>1, OutBuf, strlen(OutBuf));
  4717.     /* the filled part of the bar */
  4718.     SelectObject(hDC, hMagBrush);  /* filled part of bar */
  4719.     x = (long) (width - 2*tmFont.tmAveCharWidth) *ProPos/ProRange + tmFont.tmAveCharWidth;
  4720.     Rectangle(hDC, tmFont.tmAveCharWidth, tmFont.tmHeight <<2,
  4721.                    x, tmFont.tmHeight * 5);
  4722.     SelectObject(hDC, hGrayBrush0);  /* empty part of bar */
  4723.     Rectangle(hDC, x, tmFont.tmHeight <<2,
  4724.                    width-tmFont.tmAveCharWidth, tmFont.tmHeight * 5);
  4725.     EndPaint(hPro, (LPPAINTSTRUCT) &ps);
  4726.     return (NULL);
  4727. }
  4728.  
  4729. /****************************************************************************
  4730.  
  4731.     FUNCTION: CubeWndProc(HWND, unsigned, WORD, LONG)
  4732.  
  4733.     PURPOSE:  Processes messages
  4734.  
  4735.     COMMENTS:
  4736.  
  4737.         This is the windows procedure for the the playing cube buttons.
  4738.         The buttons change their appearance when pressed down.
  4739.         Information on whether the button is pressed or not is contained
  4740.         in the global data structure board[x][y].
  4741.  
  4742. ****************************************************************************/
  4743.  
  4744. long FAR PASCAL CubeWndProc(hCube, message, wParam, lParam)
  4745. HWND hCube;                                /* window handle                   */
  4746. unsigned message;                         /* type of message                 */
  4747. WORD wParam;                              /* additional information          */
  4748. LONG lParam;                              /* additional information          */
  4749. {
  4750.     PAINTSTRUCT ps;                       /* used by paint procedure */
  4751.     HDC hDC;                              /* used by paint procedure */
  4752.     int row, col, CubeNumber;
  4753.  
  4754.     switch (message) {
  4755.         case WM_LBUTTONDOWN:
  4756.         case WM_MBUTTONDOWN:
  4757.         case WM_RBUTTONDOWN:
  4758.             ProcessCubeButton(hCube);       
  4759.             /* no break - fall through to paint */
  4760.  
  4761.         case WM_PAINT:
  4762.             hDC = BeginPaint(hCube, (LPPAINTSTRUCT) &ps);
  4763.             ProcessCubePaint(hCube, hDC);
  4764.             EndPaint(hCube, (LPPAINTSTRUCT) &ps);
  4765.             break;
  4766.         case WM_MOUSEACTIVATE:
  4767.             /* Don't pass this to DefWindowProc, else parent gets it */
  4768.             break;
  4769.         default:                          /* Passes it on if unproccessed    */
  4770.             return (DefWindowProc(hCube, message, wParam, lParam));
  4771.        }
  4772.     return (NULL);
  4773. }
  4774.  
  4775. /* This is called when the player clicks on an enabled cube */
  4776. /* Toggles the flag saying whether button is down or not */
  4777. ProcessCubeButton(hWnd)
  4778. HWND hWnd;
  4779.    {
  4780.     int CubeNumber;     /* this is used to decode which control is responding */
  4781.     int row, col;
  4782.     WORD cubeloc;
  4783.  
  4784.     CubeNumber = GetWindowWord(hWnd, GWW_ID) - ID_FIRSTCUBE;  /* which control? */
  4785.     row = CubeNumber / NCOLS;
  4786.     col = CubeNumber % NCOLS;
  4787.     if (board[row][col].Uused)
  4788.        {
  4789.         board[row][col].Uused = FALSE;
  4790.         /* if popping up cube, erase last entry in player list */
  4791.         PostMessage(hUEdit, WM_CHAR, '\b', 1L);
  4792.         PostMessage(GetParent(hWnd), BAGOM_CUBEUP, 0, 0L);
  4793.         /* To delete a Q, must also delete the U */
  4794.         if (board[row][col].val == 'Q')
  4795.             PostMessage(hUEdit, WM_CHAR, '\b', 1L);
  4796.        }
  4797.     else
  4798.        {
  4799.         board[row][col].Uused = TRUE;
  4800.         PostMessage(hUEdit, WM_CHAR, board[row][col].val, 1L);
  4801.         cubeloc = col << 8 | row;
  4802.         PostMessage(GetParent(hWnd), BAGOM_CUBEDN, cubeloc, 0L);
  4803.         /* Q has automatic U following */
  4804.         if (board[row][col].val == 'Q')
  4805.             PostMessage(hUEdit, WM_CHAR, 'U', 1L);
  4806.        }
  4807.     InvalidateRect(hWnd, NULL, FALSE);
  4808.    }
  4809.  
  4810. /* Paint procedure for cube button controls */
  4811. ProcessCubePaint(hWnd, hDC)
  4812. HWND hWnd;
  4813. HDC hDC;
  4814.    {
  4815.     HDC hMemoryDC, hMemoryDC1;
  4816.     HBITMAP hOldBitmap, hOldBitmap1, hLetterBitmap, hBackBitmap;
  4817.     int CubeNumber;     /* this is used to decode which control is responding */
  4818.     int row, col;
  4819.     char letter;        /* the letter shown on this cube */
  4820.     int rot;            /* the rotation of the letter */
  4821.     HBITMAP hShade;     /* used only for mono */
  4822.     HBRUSH hShadeBrush, hOldBrush;
  4823.  
  4824.     CubeNumber = GetWindowWord(hWnd, GWW_ID) - ID_FIRSTCUBE;  /* which control? */
  4825.     row = CubeNumber / NCOLS;           /* what is on face? */
  4826.     col = CubeNumber % NCOLS;
  4827.     letter = board[row][col].val;
  4828.     rot = (RotCubes ? board[row][col].orient : 0);
  4829.  
  4830.     hMemoryDC = CreateCompatibleDC(hDC);
  4831.     hMemoryDC1 = CreateCompatibleDC(hDC);
  4832.     hLetterBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(STARBMP+letter-'@'+rot*26));
  4833.     hOldBitmap = SelectObject(hMemoryDC, hLetterBitmap);
  4834.     if (hOldBitmap)
  4835.        {
  4836.         if (board[row][col].Uused) hBackBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(DOWNBMP));
  4837.         else hBackBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(UPBMP));
  4838.         hOldBitmap1 = SelectObject(hMemoryDC1, hBackBitmap);
  4839.  
  4840.         if (Mono && board[row][col].Uused)
  4841.            {
  4842.             hShade = LoadBitmap(hInst, MAKEINTRESOURCE(SHADEBMP));
  4843.             hShadeBrush = CreatePatternBrush(hShade);
  4844.             hOldBrush = SelectObject(hMemoryDC1, hShadeBrush);
  4845.             BitBlt(hMemoryDC1, 0, 0, 32, 32, hMemoryDC, 0, 0, 0x008003E9);  /* DPSaa */
  4846.             SelectObject(hMemoryDC1, hOldBrush);
  4847.             DeleteObject(hShadeBrush);
  4848.             DeleteObject(hShade);
  4849.            }
  4850.         else
  4851.             BitBlt(hMemoryDC1, 0, 0, 32, 32, hMemoryDC, 0, 0, SRCAND);
  4852.  
  4853.         /* copy to display only once */
  4854.         BitBlt(hDC, 0, 0, 32, 32, hMemoryDC1, 0, 0, SRCCOPY);
  4855.         SelectObject(hMemoryDC1, hOldBitmap1);
  4856.         DeleteObject(hBackBitmap);
  4857.         SelectObject(hMemoryDC, hOldBitmap);
  4858.        }
  4859.     DeleteObject(hLetterBitmap);
  4860.     DeleteDC(hMemoryDC);
  4861.     DeleteDC(hMemoryDC1);
  4862.    }
  4863.  
  4864. /* Creates a pictoral display box. */
  4865. /* Displays the bitmap nicely, with the text centered underneath */
  4866. /* If no bitmap to display, specify PicNumber = 0 */
  4867. /* The box becomes system modal */
  4868. CreatePicBox(hWnd, PicNumber, PicMsg)
  4869. HWND hWnd;
  4870. int PicNumber;
  4871. LPSTR PicMsg;
  4872.    {
  4873.     HWND hPic;
  4874.     PICSTRUCT PicStruct;        /* init structure */
  4875.     
  4876.     PicStruct.PicMsg = PicMsg;
  4877.     PicStruct.PicNumber = PicNumber;
  4878.  
  4879.     hPic = CreateWindow("Pic",          /* window class */
  4880.         "",                             /* window name       */
  4881.         WS_POPUP | WS_BORDER | WS_VISIBLE,
  4882.         0, 0, 0, 0,     /* window will size itself */
  4883.         hWnd,                           /* parent handle        */
  4884.         NULL,                           /* no ID for popup window */
  4885.         hInst,                          /* instance             */
  4886.         (LPSTR) &PicStruct);                    /* additional info      */
  4887.  
  4888.     SetSysModalWindow(hPic);
  4889.    }
  4890.  
  4891. /****************************************************************************
  4892.  
  4893.     FUNCTION: PicWndProc(HWND, unsigned, WORD, LONG)
  4894.  
  4895.     PURPOSE:  Processes messages
  4896.  
  4897.     COMMENTS:
  4898.  
  4899.         This is the window procedure for a very simple window which
  4900.         displays a bitmap and some text.  The bitmap and text are
  4901.         passed in a structure upon initialization.
  4902.  
  4903. ****************************************************************************/
  4904.  
  4905. long FAR PASCAL PicWndProc(hPic, message, wParam, lParam)
  4906. HWND hPic;                                /* window handle                   */
  4907. unsigned message;                         /* type of message                 */
  4908. WORD wParam;                              /* additional information          */
  4909. LONG lParam;                              /* additional information          */
  4910. {
  4911.     static HBITMAP hBitmap;  /* the picture to be displayed */
  4912.     static LPPICSTRUCT lpPic;   /* the parameter passed upon create */
  4913.     static BITMAP Bitmap;               /* to retrieve bitmap dimensions */
  4914.     static char PicMsg[80];
  4915.  
  4916.     PAINTSTRUCT ps;                       /* used by paint procedure */
  4917.     HDC hDC;                              /* used by paint procedure */
  4918.  
  4919.     int x, y;                           /* placement of PicBox */
  4920.     /* TrueBmWidth compensates for aspect ratio of monitor */
  4921.     static int height, width, TrueBmWidth;
  4922.     static TEXTMETRIC tmFont;
  4923.     int textwidth;
  4924.     HWND hWnd;                          /* handle of parent */
  4925.     RECT Rect;
  4926.     POINT Point;
  4927.     HBITMAP hOldBitmap;         /* for drawing picture */
  4928.     HDC hMemoryDC;
  4929.     
  4930.     switch (message) {
  4931.  
  4932.         case WM_CREATE:
  4933.             lpPic = (LPPICSTRUCT) ((LPCREATESTRUCT) lParam) -> lpCreateParams;
  4934.             if (lpPic -> PicNumber)
  4935.                {
  4936.                 hBitmap = LoadBitmap(hInst, MAKEINTRESOURCE(lpPic -> PicNumber));
  4937.                 GetObject(hBitmap, sizeof(BITMAP), (LPSTR) &Bitmap);
  4938.                }
  4939.             else  /* no bitmap specified */
  4940.                {
  4941.                 Bitmap.bmHeight = 0;
  4942.                 Bitmap.bmWidth  = 0;
  4943.                 hBitmap = 0;
  4944.                }
  4945.             lstrcpy(PicMsg, lpPic -> PicMsg);
  4946.             hWnd = GetParent(hPic);
  4947.             GetClientRect(hWnd, (LPRECT) &Rect);
  4948.             hDC = GetDC(hPic);
  4949.             GetTextMetrics(hDC, &tmFont);       /* size the window */
  4950.             height = Bitmap.bmHeight + tmFont.tmHeight * 2 + 2; /* +2 for thin border */
  4951.             TrueBmWidth = MulDiv(Bitmap.bmWidth, GetDeviceCaps(hDC, ASPECTY), GetDeviceCaps(hDC, ASPECTX));
  4952.             width  = TrueBmWidth + 2;
  4953.             textwidth = LOWORD(GetTextExtent(hDC, PicMsg, lstrlen(PicMsg))) + 2*tmFont.tmAveCharWidth;
  4954.             /* width is larger of picture width or text width */
  4955.             if (width < textwidth) width = textwidth;
  4956.             x = (Rect.right + Rect.left - width) >> 1;  /* center the window */
  4957.             y = (Rect.bottom + Rect.top - height) >> 1;
  4958.             ReleaseDC(hPic, hDC);
  4959.             Point.x = x;        /* convert to screen coordinates for popup */
  4960.             Point.y = y;
  4961.             ClientToScreen(hWnd, &Point);
  4962.             Point.x = max(Point.x, 0);          /* in case parent too small */
  4963.             Point.y = max(Point.y, 0);
  4964.             MoveWindow(hPic, Point.x, Point.y, width, height, TRUE);
  4965.             break;
  4966.  
  4967.         case WM_PAINT:
  4968.             hDC = BeginPaint(hPic, (LPPAINTSTRUCT) &ps);
  4969.             SetBkMode(hDC, TRANSPARENT);
  4970.             SetTextAlign(hDC, TA_CENTER | TA_TOP);
  4971.             SetTextColor(hDC, WHITE);
  4972.             x = (width - 2) >> 1;
  4973.             y = height - 2 - 3*tmFont.tmHeight/2;  /* -2 because thin border */
  4974.             TextOut(hDC, x, y, PicMsg, lstrlen(PicMsg));
  4975.             /* now draw the picture, if there is one */
  4976.             if (hBitmap)
  4977.                {
  4978.                 x=(width-TrueBmWidth) >>1;      /* center the picture */
  4979.                 y=0;
  4980.                 hMemoryDC = CreateCompatibleDC(hDC);
  4981.                 hOldBitmap = SelectObject(hMemoryDC, hBitmap);
  4982.                 SetStretchBltMode(hDC, COLORONCOLOR);
  4983.                 StretchBlt(hDC, x, y, TrueBmWidth, Bitmap.bmHeight,
  4984.                     hMemoryDC, 0, 0, Bitmap.bmWidth, Bitmap.bmHeight, SRCCOPY);
  4985.                 SelectObject(hDC, hOldBitmap);
  4986.                 DeleteDC(hMemoryDC);
  4987.                }
  4988.             EndPaint(hPic, (LPPAINTSTRUCT) &ps);
  4989.             break;
  4990.  
  4991.         /* close the window with almost any excuse */
  4992.         case WM_CHAR:
  4993.         case WM_LBUTTONDOWN:
  4994.         case WM_MBUTTONDOWN:
  4995.         case WM_RBUTTONDOWN:
  4996.         case WM_CLOSE:
  4997.             if (hBitmap) DeleteObject(hBitmap);
  4998.             DestroyWindow(hPic);
  4999.             break;
  5000.  
  5001.         default:                          /* Passes it on if unproccessed    */
  5002.             return (DefWindowProc(hPic, message, wParam, lParam));
  5003.        }
  5004.     return (NULL);
  5005.    }
  5006.